Compare commits
16 Commits
441ef2b850
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 23fddbd976 | |||
| 353eb97563 | |||
| 33ba00a682 | |||
| 1191abc914 | |||
| 76ac60d3fd | |||
| 0803a0124b | |||
| 6b3b0360e7 | |||
| f323e5a82c | |||
| 09dc1de451 | |||
| 049db2e0e0 | |||
| 65c9815baa | |||
| 3794b7f29e | |||
| 709a024820 | |||
| e497f19652 | |||
| c9fd3e47cb | |||
| 1a74c03621 |
@@ -171,9 +171,40 @@ jobs:
|
|||||||
alpine:latest \
|
alpine:latest \
|
||||||
sh -lc '
|
sh -lc '
|
||||||
test -d /app/frontend
|
test -d /app/frontend
|
||||||
|
mkdir -p /app/frontend/public
|
||||||
printf "VITE_BUILD_VERSION=%s\nVITE_BUILD_DATE=%s\n" "$BUILD_VERSION" "$BUILD_DATE" > /app/frontend/.env.production
|
printf "VITE_BUILD_VERSION=%s\nVITE_BUILD_DATE=%s\n" "$BUILD_VERSION" "$BUILD_DATE" > /app/frontend/.env.production
|
||||||
|
printf "{\n \"version\": \"%s\",\n \"buildDate\": \"%s\"\n}\n" "$BUILD_VERSION" "$BUILD_DATE" > /app/frontend/public/build-meta.json
|
||||||
echo "Frontend build metadata:"
|
echo "Frontend build metadata:"
|
||||||
cat /app/frontend/.env.production
|
cat /app/frontend/.env.production
|
||||||
|
cat /app/frontend/public/build-meta.json
|
||||||
|
'
|
||||||
|
|
||||||
|
- name: Prepare deploy host disk headroom
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
docker run --rm \
|
||||||
|
-v "${DEPLOY_PATH}:/workspace/vtube-awards" \
|
||||||
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
|
-w /workspace/vtube-awards \
|
||||||
|
docker:cli \
|
||||||
|
sh -lc '
|
||||||
|
set -e
|
||||||
|
echo "Disk before cleanup:"
|
||||||
|
df -h .
|
||||||
|
|
||||||
|
mkdir -p backups
|
||||||
|
old_backups="$(ls -1t backups/predeploy-*.dump 2>/dev/null | tail -n +4 || true)"
|
||||||
|
if [ -n "$old_backups" ]; then
|
||||||
|
echo "$old_backups" | while IFS= read -r backup; do
|
||||||
|
[ -n "$backup" ] && rm -f "$backup"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker builder prune -af || true
|
||||||
|
docker image prune -af || true
|
||||||
|
|
||||||
|
echo "Disk after cleanup:"
|
||||||
|
df -h .
|
||||||
'
|
'
|
||||||
|
|
||||||
- name: Build Docker images
|
- name: Build Docker images
|
||||||
@@ -280,9 +311,11 @@ jobs:
|
|||||||
css_asset="$(printf "%s" "$index" | grep -Eo '/assets/index-[^"]+\.css' | head -n 1)"
|
css_asset="$(printf "%s" "$index" | grep -Eo '/assets/index-[^"]+\.css' | head -n 1)"
|
||||||
test -n "$js_asset"
|
test -n "$js_asset"
|
||||||
test -n "$css_asset"
|
test -n "$css_asset"
|
||||||
js_body="$(curl -fsS --max-time 20 "${LIVE_URL}${js_asset}")"
|
curl -fsS --max-time 20 "${LIVE_URL}${js_asset}" >/dev/null
|
||||||
printf "%s" "$js_body" | grep -Fq "${{ steps.meta.outputs.build_version }}"
|
|
||||||
curl -fsS --max-time 20 "${LIVE_URL}${css_asset}" >/dev/null
|
curl -fsS --max-time 20 "${LIVE_URL}${css_asset}" >/dev/null
|
||||||
|
meta_body="$(curl -fsS --max-time 15 "${LIVE_URL}/build-meta.json")"
|
||||||
|
echo "$meta_body"
|
||||||
|
echo "$meta_body" | grep -Fq '"version": "${{ steps.meta.outputs.build_version }}"'
|
||||||
echo "Frontend assets served: ${js_asset}, ${css_asset}"
|
echo "Frontend assets served: ${js_asset}, ${css_asset}"
|
||||||
echo "Frontend version verified: ${{ steps.meta.outputs.build_version }}"
|
echo "Frontend version verified: ${{ steps.meta.outputs.build_version }}"
|
||||||
|
|
||||||
|
|||||||
@@ -92,6 +92,50 @@ public static class SeasonMappings
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static (string Platform, string Slug) InferProfileMetadataFromUrl(string? value, string fallbackName = "")
|
||||||
|
{
|
||||||
|
var trimmed = value?.Trim() ?? string.Empty;
|
||||||
|
if (!Uri.TryCreate(trimmed, UriKind.Absolute, out var uri))
|
||||||
|
{
|
||||||
|
return ("Profil", fallbackName.Trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
var host = uri.Host.Trim().ToLowerInvariant();
|
||||||
|
var segments = uri.AbsolutePath
|
||||||
|
.Split('/', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||||
|
|
||||||
|
var platform = host switch
|
||||||
|
{
|
||||||
|
var item when item.Contains("twitch.tv", StringComparison.Ordinal) => "Twitch",
|
||||||
|
var item when item.Contains("youtube.com", StringComparison.Ordinal) || item.Contains("youtu.be", StringComparison.Ordinal) => "YouTube",
|
||||||
|
var item when item.Contains("x.com", StringComparison.Ordinal) || item.Contains("twitter.com", StringComparison.Ordinal) => "X",
|
||||||
|
var item when item.Contains("instagram.com", StringComparison.Ordinal) => "Instagram",
|
||||||
|
var item when item.Contains("discord.gg", StringComparison.Ordinal) || item.Contains("discord.com", StringComparison.Ordinal) => "Discord",
|
||||||
|
var item when item.Contains("kick.com", StringComparison.Ordinal) => "Kick",
|
||||||
|
var item when item.Contains("cake.gg", StringComparison.Ordinal) => "Cake",
|
||||||
|
_ => "Profil",
|
||||||
|
};
|
||||||
|
|
||||||
|
var slug = segments.LastOrDefault() ?? string.Empty;
|
||||||
|
if (string.Equals(platform, "YouTube", StringComparison.Ordinal) && segments.Length > 0)
|
||||||
|
{
|
||||||
|
slug = segments.FirstOrDefault(segment => segment.StartsWith('@')) ?? slug;
|
||||||
|
}
|
||||||
|
|
||||||
|
slug = Uri.UnescapeDataString(slug).Trim().Trim('/');
|
||||||
|
if (slug.StartsWith('@') && !string.Equals(platform, "YouTube", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
slug = slug[1..];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(slug))
|
||||||
|
{
|
||||||
|
slug = fallbackName.Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (platform, slug);
|
||||||
|
}
|
||||||
|
|
||||||
public static string NormalizeSeasonStreamUrl(string? value)
|
public static string NormalizeSeasonStreamUrl(string? value)
|
||||||
{
|
{
|
||||||
var trimmed = value?.Trim() ?? string.Empty;
|
var trimmed = value?.Trim() ?? string.Empty;
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
namespace Backend.Contracts;
|
||||||
|
|
||||||
|
public sealed record AdminArchivedWinnerItemDto(
|
||||||
|
int Id,
|
||||||
|
int Year,
|
||||||
|
string Category,
|
||||||
|
string Subcategory,
|
||||||
|
string WinnerName,
|
||||||
|
string WinnerUrl,
|
||||||
|
DateTimeOffset CreatedAt,
|
||||||
|
DateTimeOffset? UpdatedAt);
|
||||||
|
|
||||||
|
public sealed record UpsertArchivedWinnerRequest(
|
||||||
|
int Year,
|
||||||
|
string Category,
|
||||||
|
string Subcategory,
|
||||||
|
string WinnerName,
|
||||||
|
string WinnerUrl);
|
||||||
@@ -37,6 +37,8 @@ public sealed record AdminCandidateItemDto(
|
|||||||
string DisplayName,
|
string DisplayName,
|
||||||
string ChannelSlug,
|
string ChannelSlug,
|
||||||
string Platform,
|
string Platform,
|
||||||
|
int? AvgViewers,
|
||||||
|
int Votes,
|
||||||
int NominationTally,
|
int NominationTally,
|
||||||
string AcceptanceStatus,
|
string AcceptanceStatus,
|
||||||
string? AcceptanceNote,
|
string? AcceptanceNote,
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ namespace Backend.Contracts;
|
|||||||
public sealed record AdminSiteSettingsResponse(
|
public sealed record AdminSiteSettingsResponse(
|
||||||
string HostDisplayName,
|
string HostDisplayName,
|
||||||
string HostTagline,
|
string HostTagline,
|
||||||
|
string HostArtistName,
|
||||||
|
string HostImageUrl,
|
||||||
string NewsletterUrl,
|
string NewsletterUrl,
|
||||||
string ShareXUrl,
|
string ShareXUrl,
|
||||||
string ShareDiscordUrl,
|
string ShareDiscordUrl,
|
||||||
@@ -41,6 +43,7 @@ public sealed record AdminSiteSettingsResponse(
|
|||||||
public sealed record UpdateSiteSettingsRequest(
|
public sealed record UpdateSiteSettingsRequest(
|
||||||
string HostDisplayName,
|
string HostDisplayName,
|
||||||
string HostTagline,
|
string HostTagline,
|
||||||
|
string HostArtistName,
|
||||||
string NewsletterUrl,
|
string NewsletterUrl,
|
||||||
string ShareXUrl,
|
string ShareXUrl,
|
||||||
string ShareDiscordUrl,
|
string ShareDiscordUrl,
|
||||||
|
|||||||
@@ -64,6 +64,8 @@ public sealed record PublicStreamBannerContentDto(
|
|||||||
public sealed record PublicSiteContentDto(
|
public sealed record PublicSiteContentDto(
|
||||||
string HostDisplayName,
|
string HostDisplayName,
|
||||||
string HostTagline,
|
string HostTagline,
|
||||||
|
string HostArtistName,
|
||||||
|
string HostImageUrl,
|
||||||
string NewsletterUrl,
|
string NewsletterUrl,
|
||||||
string ShareXUrl,
|
string ShareXUrl,
|
||||||
string ShareDiscordUrl,
|
string ShareDiscordUrl,
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ public sealed class AwardsDbContext(DbContextOptions<AwardsDbContext> options) :
|
|||||||
public DbSet<Candidate> Candidates => Set<Candidate>();
|
public DbSet<Candidate> Candidates => Set<Candidate>();
|
||||||
public DbSet<StreamerIdentity> StreamerIdentities => Set<StreamerIdentity>();
|
public DbSet<StreamerIdentity> StreamerIdentities => Set<StreamerIdentity>();
|
||||||
public DbSet<AwardResult> Results => Set<AwardResult>();
|
public DbSet<AwardResult> Results => Set<AwardResult>();
|
||||||
|
public DbSet<ArchivedWinner> ArchivedWinners => Set<ArchivedWinner>();
|
||||||
public DbSet<Nomination> Nominations => Set<Nomination>();
|
public DbSet<Nomination> Nominations => Set<Nomination>();
|
||||||
public DbSet<VoteBallot> VoteBallots => Set<VoteBallot>();
|
public DbSet<VoteBallot> VoteBallots => Set<VoteBallot>();
|
||||||
public DbSet<VoteEntry> VoteEntries => Set<VoteEntry>();
|
public DbSet<VoteEntry> VoteEntries => Set<VoteEntry>();
|
||||||
@@ -40,6 +41,9 @@ public sealed class AwardsDbContext(DbContextOptions<AwardsDbContext> options) :
|
|||||||
{
|
{
|
||||||
entity.Property(item => item.HostDisplayName).HasMaxLength(120);
|
entity.Property(item => item.HostDisplayName).HasMaxLength(120);
|
||||||
entity.Property(item => item.HostTagline).HasMaxLength(160);
|
entity.Property(item => item.HostTagline).HasMaxLength(160);
|
||||||
|
entity.Property(item => item.HostArtistName).HasMaxLength(120);
|
||||||
|
entity.Property(item => item.HostImageData).HasColumnType("bytea");
|
||||||
|
entity.Property(item => item.HostImageContentType).HasMaxLength(80);
|
||||||
entity.Property(item => item.NewsletterUrl).HasMaxLength(400);
|
entity.Property(item => item.NewsletterUrl).HasMaxLength(400);
|
||||||
entity.Property(item => item.PrivacyEmail).HasMaxLength(160);
|
entity.Property(item => item.PrivacyEmail).HasMaxLength(160);
|
||||||
entity.Property(item => item.PrivacyPolicyUpdatedBy).HasMaxLength(120);
|
entity.Property(item => item.PrivacyPolicyUpdatedBy).HasMaxLength(120);
|
||||||
@@ -186,6 +190,15 @@ public sealed class AwardsDbContext(DbContextOptions<AwardsDbContext> options) :
|
|||||||
entity.Property(item => item.CategoryName).HasMaxLength(120);
|
entity.Property(item => item.CategoryName).HasMaxLength(120);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity<ArchivedWinner>(entity =>
|
||||||
|
{
|
||||||
|
entity.HasIndex(item => new { item.Year, item.Category, item.Subcategory }).IsUnique();
|
||||||
|
entity.Property(item => item.Category).HasMaxLength(120);
|
||||||
|
entity.Property(item => item.Subcategory).HasMaxLength(120);
|
||||||
|
entity.Property(item => item.WinnerName).HasMaxLength(120);
|
||||||
|
entity.Property(item => item.WinnerUrl).HasMaxLength(500);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity<UserSession>(entity =>
|
modelBuilder.Entity<UserSession>(entity =>
|
||||||
{
|
{
|
||||||
entity.HasIndex(item => item.SessionToken).IsUnique();
|
entity.HasIndex(item => item.SessionToken).IsUnique();
|
||||||
|
|||||||
@@ -0,0 +1,184 @@
|
|||||||
|
namespace Backend.Data;
|
||||||
|
|
||||||
|
using Backend.Services;
|
||||||
|
|
||||||
|
internal sealed record AwardCategorySeed(string LegacyGroupName, string Name, string Slug, string Description, int SortOrder);
|
||||||
|
internal sealed record CandidateSeed(string CategorySlug, string DisplayName, string ChannelSlug, string Platform);
|
||||||
|
internal sealed record WinnerSeed(int Year, string CategorySlug, string DisplayName, string ChannelSlug, string Platform);
|
||||||
|
internal sealed record SiteFaqSeed(string Question, string Answer);
|
||||||
|
internal sealed record SiteSocialSeed(string Label, string Platform, string Url, string Icon);
|
||||||
|
internal sealed record SponsorSeed(string Name, string WebsiteUrl, string LogoUrl, string Description, string Tier, int SortOrder);
|
||||||
|
|
||||||
|
internal static class SeedCatalog
|
||||||
|
{
|
||||||
|
internal static readonly SeasonSubcategoryTemplateSetting[] DefaultSubcategoryTemplates =
|
||||||
|
[
|
||||||
|
new("Hidden Star", "hidden-star", 1, 1, 20),
|
||||||
|
new("Rising Star", "rising-star", 2, 21, 60),
|
||||||
|
new("Shining Star", "shining-star", 3, 61, null),
|
||||||
|
];
|
||||||
|
|
||||||
|
internal static readonly AwardCategorySeed[] AwardCategorySeeds =
|
||||||
|
[
|
||||||
|
new("Main Awards", "VTuber des Jahres", "vtuber-des-jahres", "Die größte Auszeichnung des Jahres.", 1),
|
||||||
|
new("Discovery", "Best Newcomer", "best-newcomer", "Neue Stimmen, neue Welten und frische Energie für die Szene.", 2),
|
||||||
|
new("Creative", "Model & Design", "model-design", "Live2D, 3D, Outfit, Rigging und visuelle Identitaet.", 3),
|
||||||
|
new("Performance", "Gesang & Musik", "gesang-musik", "Songs, Covers, Konzerte und musikalische Highlights.", 4),
|
||||||
|
new("Gaming", "Best Gaming", "best-gaming", "Gameplay, Skill, Chaos und legendaere Gaming-Momente.", 5),
|
||||||
|
new("Entertainment", "Best Variety", "best-variety", "Talk, Comedy, Watchalongs und kreative Streamformate.", 6),
|
||||||
|
new("Community", "Community Liebling", "community-liebling", "Creator:innen, die ihre Community besonders stark verbinden.", 7),
|
||||||
|
new("Collab", "Best Collab & Duo", "best-collab-duo", "Gemeinsame Streams, Projekte und Duo-Dynamik.", 8),
|
||||||
|
];
|
||||||
|
|
||||||
|
internal static readonly Dictionary<string, string> LegacyCategorySlugMap = new(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
["bestes-live-event"] = "best-newcomer",
|
||||||
|
["clip-des-jahres"] = "model-design",
|
||||||
|
["beste-community"] = "gesang-musik",
|
||||||
|
};
|
||||||
|
|
||||||
|
internal static readonly SiteFaqSeed[] SiteFaqSeeds =
|
||||||
|
[
|
||||||
|
new(
|
||||||
|
"Wer darf nominiert werden?",
|
||||||
|
"Jede:r aktive deutschsprachige VTuber kann nominiert werden — unabhaengig von Follower-Zahl oder Plattform. Die Community schlaegt in der Nominierungsphase ihre Favorit:innen vor."),
|
||||||
|
new(
|
||||||
|
"Wie funktioniert das Voting?",
|
||||||
|
"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(
|
||||||
|
"Was kostet die Teilnahme?",
|
||||||
|
"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(
|
||||||
|
"Wann und wo findet die Award-Show statt?",
|
||||||
|
"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(
|
||||||
|
"Ich wurde nominiert — was nun?",
|
||||||
|
"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."),
|
||||||
|
];
|
||||||
|
|
||||||
|
internal static readonly SiteSocialSeed[] SiteSocialSeeds =
|
||||||
|
[
|
||||||
|
new("Twitch", "twitch", "https://twitch.tv/jayuhime", "twitch"),
|
||||||
|
new("YouTube", "youtube", "https://youtube.com/c/Jayuhime", "youtube"),
|
||||||
|
new("X", "x", "https://x.com/jayuhime", "x"),
|
||||||
|
new("Instagram", "instagram", "https://instagram.com/jayuhime", "instagram"),
|
||||||
|
new("Discord", "discord", "https://discord.gg/jayuhime", "discord"),
|
||||||
|
];
|
||||||
|
|
||||||
|
internal const string DefaultImprintContent = """
|
||||||
|
Anbieter
|
||||||
|
VTuber Star Awards, vertreten durch Jayuhime.
|
||||||
|
|
||||||
|
Kontakt
|
||||||
|
Nutze für organisatorische Fragen bitte die Kontaktseite oder die hinterlegte Kontaktadresse.
|
||||||
|
|
||||||
|
Hinweis
|
||||||
|
Dieses Impressum ist ein redaktioneller Platzhalter und sollte vor dem Livegang mit den tatsächlichen Anbieterangaben ersetzt werden.
|
||||||
|
""";
|
||||||
|
|
||||||
|
internal const string DefaultContactContent = """
|
||||||
|
Kontakt zum Award-Team
|
||||||
|
Du hast Fragen zur Nominierung, zum Voting, zu Clips oder zur Show? Schreib dem Team über die hinterlegte Kontaktseite.
|
||||||
|
|
||||||
|
Datenschutzfragen
|
||||||
|
Für Datenschutzanfragen nutze bitte die Datenschutz-E-Mail aus diesem Footer.
|
||||||
|
|
||||||
|
Community & Kooperationen
|
||||||
|
Social Links und Partneranfragen werden vom Admin-Team gepflegt und koordiniert.
|
||||||
|
""";
|
||||||
|
|
||||||
|
internal const string DefaultSponsorsContent = """
|
||||||
|
Sponsoren & Partner
|
||||||
|
Hier können Sponsor:innen, Medienpartner, Community-Partner und Supporter des VTuber Star Awards vorgestellt werden.
|
||||||
|
|
||||||
|
Partner werden im Rahmen der Show und auf den öffentlichen Kontaktflächen genannt, sobald sie final bestätigt sind.
|
||||||
|
""";
|
||||||
|
|
||||||
|
internal static readonly CandidateSeed[] CurrentCandidateSeeds =
|
||||||
|
[
|
||||||
|
new("vtuber-des-jahres-shining-star", "Hoshimi Miyu", "@hoshimimiyu", "Twitch"),
|
||||||
|
new("vtuber-des-jahres-shining-star", "Kurainu", "@kurainu", "Twitch"),
|
||||||
|
new("vtuber-des-jahres-shining-star", "Shiro Ch.", "@shiroch", "Twitch"),
|
||||||
|
new("best-newcomer-hidden-star", "Aoi Sakura", "@aoisakura", "YouTube"),
|
||||||
|
new("best-newcomer-rising-star", "Nox Live", "@noxlive", "Twitch"),
|
||||||
|
new("model-design-shining-star", "Velvet Rei", "@velvetrei", "Twitch"),
|
||||||
|
new("model-design-rising-star", "Mochi Atelier", "@mochiatelier", "Cake"),
|
||||||
|
new("gesang-musik-shining-star", "Melo Diva", "@melodiva", "YouTube"),
|
||||||
|
new("gesang-musik-rising-star", "Yuki Stern", "@yukistern", "Twitch"),
|
||||||
|
new("best-gaming-shining-star", "Kurainu", "@kurainu", "Twitch"),
|
||||||
|
new("best-gaming-rising-star", "PixelPunk", "@pixelpunk", "Twitch"),
|
||||||
|
new("best-variety-shining-star", "Taro Chaos", "@tarochaos", "Twitch"),
|
||||||
|
new("best-variety-rising-star", "Kotaro Plays", "@kotaroplays", "YouTube"),
|
||||||
|
new("community-liebling-shining-star", "Shiro Ch.", "@shiroch", "Twitch"),
|
||||||
|
new("community-liebling-hidden-star", "Lumi", "@lumi_vt", "Cake"),
|
||||||
|
new("best-collab-duo-shining-star", "Akari & Nox", "@akari_vt", "Twitch"),
|
||||||
|
new("best-collab-duo-rising-star", "Mochi & Hana", "@mochi_mochi", "YouTube"),
|
||||||
|
];
|
||||||
|
|
||||||
|
internal static readonly WinnerSeed[] WinnerSeeds =
|
||||||
|
[
|
||||||
|
new(2025, "vtuber-des-jahres-shining-star", "Hoshimi Miyu", "@hoshimimiyu", "Twitch"),
|
||||||
|
new(2025, "best-newcomer-hidden-star", "Aoi Sakura", "@aoisakura", "YouTube"),
|
||||||
|
new(2025, "model-design-shining-star", "Velvet Rei", "@velvetrei", "Twitch"),
|
||||||
|
new(2025, "gesang-musik-shining-star", "Melo Diva", "@melodiva", "YouTube"),
|
||||||
|
new(2025, "best-gaming-shining-star", "Kurainu", "@kurainu", "Twitch"),
|
||||||
|
new(2025, "best-variety-shining-star", "Taro Chaos", "@tarochaos", "Twitch"),
|
||||||
|
new(2025, "community-liebling-shining-star", "Shiro Ch.", "@shiroch", "Twitch"),
|
||||||
|
new(2025, "best-collab-duo-shining-star", "Akari & Nox", "@akari_vt", "Cake"),
|
||||||
|
new(2024, "vtuber-des-jahres-shining-star", "Aoi Sakura", "@aoisakura", "YouTube"),
|
||||||
|
new(2024, "best-newcomer-hidden-star", "Lumi", "@lumi_vt", "Cake"),
|
||||||
|
new(2024, "model-design-rising-star", "Mochi Atelier", "@mochiatelier", "Cake"),
|
||||||
|
new(2024, "gesang-musik-rising-star", "Yuki Stern", "@yukistern", "Twitch"),
|
||||||
|
new(2024, "best-gaming-shining-star", "Starbyte", "@starbyte", "Twitch"),
|
||||||
|
new(2024, "best-variety-rising-star", "Kotaro Plays", "@kotaroplays", "YouTube"),
|
||||||
|
new(2024, "community-liebling-rising-star", "Moonrelay", "@moonrelay", "Twitch"),
|
||||||
|
new(2024, "best-collab-duo-rising-star", "Pixel & Kotaro", "@pixelpunk", "Twitch"),
|
||||||
|
new(2023, "vtuber-des-jahres-shining-star", "Akari Nova", "@akarinova", "Twitch"),
|
||||||
|
new(2023, "best-newcomer-rising-star", "Nox Live", "@noxlive", "Twitch"),
|
||||||
|
new(2023, "model-design-shining-star", "Rei Velvet", "@reivelvet", "YouTube"),
|
||||||
|
new(2023, "gesang-musik-shining-star", "Tenshi Vox", "@tenshivox", "Twitch"),
|
||||||
|
new(2023, "best-gaming-rising-star", "Bit Knight", "@bitknight", "Twitch"),
|
||||||
|
new(2023, "best-variety-hidden-star", "Hana Hearts", "@hanahearts", "Cake"),
|
||||||
|
new(2023, "community-liebling-rising-star", "Sora Blau", "@sorablau", "YouTube"),
|
||||||
|
new(2023, "best-collab-duo-rising-star", "Yuki & Melo", "@yukistern", "Twitch"),
|
||||||
|
];
|
||||||
|
|
||||||
|
internal static readonly SponsorSeed[] DemoSponsorSeeds =
|
||||||
|
[
|
||||||
|
new(
|
||||||
|
"HoshiForge Studio",
|
||||||
|
"https://hoshiforge.example",
|
||||||
|
"/demo/sponsors/hoshiforge-studio.svg",
|
||||||
|
"Branding-, Overlay- und Debuet-Visuals fuer VTuber-Projekte und Community-Events.",
|
||||||
|
"Presenting Sponsor",
|
||||||
|
10),
|
||||||
|
new(
|
||||||
|
"NekoPixel Energy",
|
||||||
|
"https://nekopixel.example",
|
||||||
|
"/demo/sponsors/nekopixel-energy.svg",
|
||||||
|
"Community-fokussierter Drink-Partner fuer lange Showabende, Watchpartys und Creator-Collabs.",
|
||||||
|
"Gold Partner",
|
||||||
|
20),
|
||||||
|
new(
|
||||||
|
"PrismLoop Audio",
|
||||||
|
"https://prismloop.example",
|
||||||
|
"/demo/sponsors/prismloop-audio.svg",
|
||||||
|
"Audio-Tools, Intro-Packs und Stream-Sounddesign fuer Live-Shows und Highlight-Clips.",
|
||||||
|
"Gold Partner",
|
||||||
|
30),
|
||||||
|
new(
|
||||||
|
"CloudBeacon Hosting",
|
||||||
|
"https://cloudbeacon.example",
|
||||||
|
"/demo/sponsors/cloudbeacon-hosting.svg",
|
||||||
|
"Skalierbares Hosting fuer Voting, Landingpages und Event-Traffic rund um Showtage.",
|
||||||
|
"Tech Partner",
|
||||||
|
40),
|
||||||
|
new(
|
||||||
|
"ChibiCanvas Market",
|
||||||
|
"https://chibicanvas.example",
|
||||||
|
"/demo/sponsors/chibicanvas-market.svg",
|
||||||
|
"Merch-, Sticker- und Artist-Marketplace mit Fokus auf VTuber, Emotes und Fanartikel.",
|
||||||
|
"Community Partner",
|
||||||
|
50),
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
namespace Backend.Domain;
|
||||||
|
|
||||||
|
public sealed class ArchivedWinner
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public int Year { get; set; }
|
||||||
|
public string Category { get; set; } = string.Empty;
|
||||||
|
public string Subcategory { get; set; } = string.Empty;
|
||||||
|
public string WinnerName { get; set; } = string.Empty;
|
||||||
|
public string WinnerUrl { get; set; } = string.Empty;
|
||||||
|
public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow;
|
||||||
|
public DateTimeOffset? UpdatedAt { get; set; }
|
||||||
|
}
|
||||||
@@ -5,6 +5,10 @@ public sealed class SiteSettings
|
|||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
public string HostDisplayName { get; set; } = string.Empty;
|
public string HostDisplayName { get; set; } = string.Empty;
|
||||||
public string HostTagline { get; set; } = string.Empty;
|
public string HostTagline { get; set; } = string.Empty;
|
||||||
|
public string HostArtistName { get; set; } = string.Empty;
|
||||||
|
public byte[]? HostImageData { get; set; }
|
||||||
|
public string? HostImageContentType { get; set; }
|
||||||
|
public DateTimeOffset? HostImageUpdatedAt { get; set; }
|
||||||
public string NewsletterUrl { get; set; } = string.Empty;
|
public string NewsletterUrl { get; set; } = string.Empty;
|
||||||
public string ShareXUrl { get; set; } = string.Empty;
|
public string ShareXUrl { get; set; } = string.Empty;
|
||||||
public string ShareDiscordUrl { get; set; } = string.Empty;
|
public string ShareDiscordUrl { get; set; } = string.Empty;
|
||||||
|
|||||||
@@ -0,0 +1,249 @@
|
|||||||
|
using Backend.Common;
|
||||||
|
using Backend.Contracts;
|
||||||
|
using Backend.Data;
|
||||||
|
using Backend.Domain;
|
||||||
|
using Backend.Security;
|
||||||
|
using Backend.Services;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace Backend.Endpoints;
|
||||||
|
|
||||||
|
public static class AdminArchiveEndpoints
|
||||||
|
{
|
||||||
|
public static RouteGroupBuilder MapAdminArchiveEndpoints(this RouteGroupBuilder group)
|
||||||
|
{
|
||||||
|
group.MapGet("/archived-winners", GetArchivedWinners)
|
||||||
|
.AddEndpointFilter((context, next) => AdminEndpointConventions.RequirePermission(context, next, AdminPermissionCatalog.Winners))
|
||||||
|
.WithName("GetAdminArchivedWinners");
|
||||||
|
|
||||||
|
group.MapPost("/archived-winners", CreateArchivedWinner)
|
||||||
|
.AddEndpointFilter((context, next) => AdminEndpointConventions.RequireWritePermission(context, next, AdminPermissionCatalog.Winners))
|
||||||
|
.WithName("CreateAdminArchivedWinner");
|
||||||
|
|
||||||
|
group.MapPut("/archived-winners/{archivedWinnerId:int}", UpdateArchivedWinner)
|
||||||
|
.AddEndpointFilter((context, next) => AdminEndpointConventions.RequireWritePermission(context, next, AdminPermissionCatalog.Winners))
|
||||||
|
.WithName("UpdateAdminArchivedWinner");
|
||||||
|
|
||||||
|
group.MapDelete("/archived-winners/{archivedWinnerId:int}", DeleteArchivedWinner)
|
||||||
|
.AddEndpointFilter((context, next) => AdminEndpointConventions.RequireWritePermission(context, next, AdminPermissionCatalog.Winners))
|
||||||
|
.WithName("DeleteAdminArchivedWinner");
|
||||||
|
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<IResult> GetArchivedWinners(AwardsDbContext db, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var items = await db.ArchivedWinners
|
||||||
|
.AsNoTracking()
|
||||||
|
.OrderByDescending(item => item.Year)
|
||||||
|
.ThenBy(item => item.Category)
|
||||||
|
.ThenBy(item => item.Subcategory)
|
||||||
|
.ThenBy(item => item.WinnerName)
|
||||||
|
.Select(item => ToDto(item))
|
||||||
|
.ToArrayAsync(cancellationToken);
|
||||||
|
|
||||||
|
return Results.Ok(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<IResult> CreateArchivedWinner(
|
||||||
|
HttpContext context,
|
||||||
|
UpsertArchivedWinnerRequest request,
|
||||||
|
AwardsDbContext db,
|
||||||
|
IAdminAuditService adminAuditService)
|
||||||
|
{
|
||||||
|
var session = AdminEndpointConventions.CurrentSession(context);
|
||||||
|
var validation = ValidateRequest(request);
|
||||||
|
if (validation is not null)
|
||||||
|
{
|
||||||
|
return validation;
|
||||||
|
}
|
||||||
|
|
||||||
|
var normalized = NormalizeRequest(request);
|
||||||
|
var duplicateExists = await db.ArchivedWinners.AnyAsync(item =>
|
||||||
|
item.Year == normalized.Year
|
||||||
|
&& item.Category == normalized.Category
|
||||||
|
&& item.Subcategory == normalized.Subcategory,
|
||||||
|
context.RequestAborted);
|
||||||
|
if (duplicateExists)
|
||||||
|
{
|
||||||
|
return Results.BadRequest(new { message = "Für dieses Jahr, diese Kategorie und Unterkategorie existiert bereits ein Archivgewinner." });
|
||||||
|
}
|
||||||
|
|
||||||
|
var archivedWinner = new ArchivedWinner
|
||||||
|
{
|
||||||
|
Year = normalized.Year,
|
||||||
|
Category = normalized.Category,
|
||||||
|
Subcategory = normalized.Subcategory,
|
||||||
|
WinnerName = normalized.WinnerName,
|
||||||
|
WinnerUrl = normalized.WinnerUrl,
|
||||||
|
};
|
||||||
|
|
||||||
|
db.ArchivedWinners.Add(archivedWinner);
|
||||||
|
|
||||||
|
adminAuditService.AddEntry(
|
||||||
|
session.TwitchUserId,
|
||||||
|
"archived-winner.create",
|
||||||
|
"archivedWinner",
|
||||||
|
$"{normalized.Year}:{normalized.Category}:{normalized.Subcategory}",
|
||||||
|
$"Archivgewinner {normalized.Year} · {normalized.Category} · {normalized.Subcategory} angelegt.",
|
||||||
|
new
|
||||||
|
{
|
||||||
|
normalized.Year,
|
||||||
|
normalized.Category,
|
||||||
|
normalized.Subcategory,
|
||||||
|
normalized.WinnerName,
|
||||||
|
normalized.WinnerUrl,
|
||||||
|
},
|
||||||
|
RequestMetadataReader.Read(context));
|
||||||
|
|
||||||
|
await db.SaveChangesAsync(context.RequestAborted);
|
||||||
|
return Results.Ok(new { saved = true, entry = ToDto(archivedWinner) });
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<IResult> UpdateArchivedWinner(
|
||||||
|
HttpContext context,
|
||||||
|
int archivedWinnerId,
|
||||||
|
UpsertArchivedWinnerRequest request,
|
||||||
|
AwardsDbContext db,
|
||||||
|
IAdminAuditService adminAuditService)
|
||||||
|
{
|
||||||
|
var session = AdminEndpointConventions.CurrentSession(context);
|
||||||
|
var validation = ValidateRequest(request);
|
||||||
|
if (validation is not null)
|
||||||
|
{
|
||||||
|
return validation;
|
||||||
|
}
|
||||||
|
|
||||||
|
var archivedWinner = await db.ArchivedWinners.FirstOrDefaultAsync(item => item.Id == archivedWinnerId, context.RequestAborted);
|
||||||
|
if (archivedWinner is null)
|
||||||
|
{
|
||||||
|
return Results.NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
var normalized = NormalizeRequest(request);
|
||||||
|
var duplicateExists = await db.ArchivedWinners.AnyAsync(item =>
|
||||||
|
item.Id != archivedWinnerId
|
||||||
|
&& item.Year == normalized.Year
|
||||||
|
&& item.Category == normalized.Category
|
||||||
|
&& item.Subcategory == normalized.Subcategory,
|
||||||
|
context.RequestAborted);
|
||||||
|
if (duplicateExists)
|
||||||
|
{
|
||||||
|
return Results.BadRequest(new { message = "Für dieses Jahr, diese Kategorie und Unterkategorie existiert bereits ein Archivgewinner." });
|
||||||
|
}
|
||||||
|
|
||||||
|
archivedWinner.Year = normalized.Year;
|
||||||
|
archivedWinner.Category = normalized.Category;
|
||||||
|
archivedWinner.Subcategory = normalized.Subcategory;
|
||||||
|
archivedWinner.WinnerName = normalized.WinnerName;
|
||||||
|
archivedWinner.WinnerUrl = normalized.WinnerUrl;
|
||||||
|
archivedWinner.UpdatedAt = DateTimeOffset.UtcNow;
|
||||||
|
|
||||||
|
adminAuditService.AddEntry(
|
||||||
|
session.TwitchUserId,
|
||||||
|
"archived-winner.update",
|
||||||
|
"archivedWinner",
|
||||||
|
archivedWinner.Id.ToString(),
|
||||||
|
$"Archivgewinner {normalized.Year} · {normalized.Category} · {normalized.Subcategory} aktualisiert.",
|
||||||
|
new
|
||||||
|
{
|
||||||
|
archivedWinner.Id,
|
||||||
|
normalized.Year,
|
||||||
|
normalized.Category,
|
||||||
|
normalized.Subcategory,
|
||||||
|
normalized.WinnerName,
|
||||||
|
normalized.WinnerUrl,
|
||||||
|
},
|
||||||
|
RequestMetadataReader.Read(context));
|
||||||
|
|
||||||
|
await db.SaveChangesAsync(context.RequestAborted);
|
||||||
|
return Results.Ok(new { saved = true, entry = ToDto(archivedWinner) });
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<IResult> DeleteArchivedWinner(
|
||||||
|
HttpContext context,
|
||||||
|
int archivedWinnerId,
|
||||||
|
AwardsDbContext db,
|
||||||
|
IAdminAuditService adminAuditService)
|
||||||
|
{
|
||||||
|
var session = AdminEndpointConventions.CurrentSession(context);
|
||||||
|
var archivedWinner = await db.ArchivedWinners.FirstOrDefaultAsync(item => item.Id == archivedWinnerId, context.RequestAborted);
|
||||||
|
if (archivedWinner is null)
|
||||||
|
{
|
||||||
|
return Results.NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
db.ArchivedWinners.Remove(archivedWinner);
|
||||||
|
|
||||||
|
adminAuditService.AddEntry(
|
||||||
|
session.TwitchUserId,
|
||||||
|
"archived-winner.delete",
|
||||||
|
"archivedWinner",
|
||||||
|
archivedWinner.Id.ToString(),
|
||||||
|
$"Archivgewinner {archivedWinner.Year} · {archivedWinner.Category} · {archivedWinner.Subcategory} gelöscht.",
|
||||||
|
new
|
||||||
|
{
|
||||||
|
archivedWinner.Id,
|
||||||
|
archivedWinner.Year,
|
||||||
|
archivedWinner.Category,
|
||||||
|
archivedWinner.Subcategory,
|
||||||
|
archivedWinner.WinnerName,
|
||||||
|
archivedWinner.WinnerUrl,
|
||||||
|
},
|
||||||
|
RequestMetadataReader.Read(context));
|
||||||
|
|
||||||
|
await db.SaveChangesAsync(context.RequestAborted);
|
||||||
|
return Results.Ok(new { deleted = true, archivedWinnerId });
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AdminArchivedWinnerItemDto ToDto(ArchivedWinner item) =>
|
||||||
|
new(
|
||||||
|
item.Id,
|
||||||
|
item.Year,
|
||||||
|
item.Category,
|
||||||
|
item.Subcategory,
|
||||||
|
item.WinnerName,
|
||||||
|
item.WinnerUrl,
|
||||||
|
item.CreatedAt,
|
||||||
|
item.UpdatedAt);
|
||||||
|
|
||||||
|
private static IResult? ValidateRequest(UpsertArchivedWinnerRequest request)
|
||||||
|
{
|
||||||
|
if (request.Year < 2000 || request.Year > 3000)
|
||||||
|
{
|
||||||
|
return Results.BadRequest(new { message = "Bitte ein gültiges Archivjahr angeben." });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(request.Category))
|
||||||
|
{
|
||||||
|
return Results.BadRequest(new { message = "Die Kategorie darf nicht leer sein." });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(request.Subcategory))
|
||||||
|
{
|
||||||
|
return Results.BadRequest(new { message = "Die Unterkategorie darf nicht leer sein." });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(request.WinnerName))
|
||||||
|
{
|
||||||
|
return Results.BadRequest(new { message = "Der Gewinnername darf nicht leer sein." });
|
||||||
|
}
|
||||||
|
|
||||||
|
var winnerUrl = request.WinnerUrl?.Trim() ?? string.Empty;
|
||||||
|
if (!Uri.TryCreate(winnerUrl, UriKind.Absolute, out var uri)
|
||||||
|
|| (uri.Scheme != Uri.UriSchemeHttp && uri.Scheme != Uri.UriSchemeHttps))
|
||||||
|
{
|
||||||
|
return Results.BadRequest(new { message = "Bitte einen gültigen http- oder https-Link angeben." });
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static (int Year, string Category, string Subcategory, string WinnerName, string WinnerUrl) NormalizeRequest(UpsertArchivedWinnerRequest request) =>
|
||||||
|
(
|
||||||
|
request.Year,
|
||||||
|
SeasonMappings.NormalizePlainTextContent(request.Category),
|
||||||
|
SeasonMappings.NormalizePlainTextContent(request.Subcategory),
|
||||||
|
SeasonMappings.NormalizePlainTextContent(request.WinnerName),
|
||||||
|
request.WinnerUrl.Trim());
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@ public static class AdminEndpoints
|
|||||||
|
|
||||||
group.MapAdminDashboardEndpoints();
|
group.MapAdminDashboardEndpoints();
|
||||||
group.MapAdminSeasonManagementEndpoints();
|
group.MapAdminSeasonManagementEndpoints();
|
||||||
|
group.MapAdminArchiveEndpoints();
|
||||||
group.MapAdminModerationEndpoints();
|
group.MapAdminModerationEndpoints();
|
||||||
group.MapAdminExtrasEndpoints();
|
group.MapAdminExtrasEndpoints();
|
||||||
group.MapAdminTeamEndpoints();
|
group.MapAdminTeamEndpoints();
|
||||||
|
|||||||
@@ -28,13 +28,6 @@ public static partial class AdminModerationEndpoints
|
|||||||
return Results.NotFound();
|
return Results.NotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
var trackingFlags = TrackingRulesSettings.ReadFlagHits(nomination.TrackingFlagsJson);
|
|
||||||
if (trackingFlags.Any(flag => flag.BlocksApproval)
|
|
||||||
&& !string.Equals(nomination.TrackingReviewStatus, "overridden", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
return Results.BadRequest(new { message = "Tracking Rules blockieren die Freigabe. Bitte setze zuerst einen manuellen Override im Review." });
|
|
||||||
}
|
|
||||||
|
|
||||||
var rawDisplayName = FirstNonEmpty(request.DisplayName, nomination.CandidateText, nomination.ResolvedChannel);
|
var rawDisplayName = FirstNonEmpty(request.DisplayName, nomination.CandidateText, nomination.ResolvedChannel);
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(rawDisplayName))
|
if (string.IsNullOrWhiteSpace(rawDisplayName))
|
||||||
|
|||||||
@@ -151,10 +151,15 @@ public static partial class AdminModerationEndpoints
|
|||||||
|
|
||||||
private static async Task<IResult> BulkResolveRiskFlags(
|
private static async Task<IResult> BulkResolveRiskFlags(
|
||||||
HttpContext context,
|
HttpContext context,
|
||||||
BulkResolveRiskFlagsRequest request,
|
BulkResolveRiskFlagsRequest? request,
|
||||||
AwardsDbContext db,
|
AwardsDbContext db,
|
||||||
IAdminAuditService adminAuditService)
|
IAdminAuditService adminAuditService)
|
||||||
{
|
{
|
||||||
|
if (request is null || request.RiskFlagIds is null)
|
||||||
|
{
|
||||||
|
return Results.BadRequest(new { message = "Bulk-Verarbeitung braucht mindestens einen Risikohinweis." });
|
||||||
|
}
|
||||||
|
|
||||||
var session = AdminEndpointConventions.CurrentSession(context);
|
var session = AdminEndpointConventions.CurrentSession(context);
|
||||||
var normalizedIds = request.RiskFlagIds
|
var normalizedIds = request.RiskFlagIds
|
||||||
.Distinct()
|
.Distinct()
|
||||||
@@ -188,7 +193,7 @@ public static partial class AdminModerationEndpoints
|
|||||||
|
|
||||||
if (riskFlags.Any(item => item.Severity != "low"))
|
if (riskFlags.Any(item => item.Severity != "low"))
|
||||||
{
|
{
|
||||||
return Results.BadRequest(new { message = "Bulk-Verarbeitung ist nur fuer Low-Severity-Hinweise erlaubt." });
|
return Results.BadRequest(new { message = "Bulk-Verarbeitung ist nur fuer offene Low-Severity-Hinweise erlaubt." });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (riskFlags.Any(item => item.Status != "open"))
|
if (riskFlags.Any(item => item.Status != "open"))
|
||||||
|
|||||||
@@ -193,6 +193,34 @@ public static partial class AdminSeasonManagementEndpoints
|
|||||||
return Results.Ok(new { saved = true, candidateId = candidate.Id });
|
return Results.Ok(new { saved = true, candidateId = candidate.Id });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static async Task<IResult> GetCandidateDeletePreview(
|
||||||
|
int candidateId,
|
||||||
|
AwardsDbContext db,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var candidate = await db.Candidates
|
||||||
|
.AsNoTracking()
|
||||||
|
.FirstOrDefaultAsync(item => item.Id == candidateId, cancellationToken);
|
||||||
|
if (candidate is null)
|
||||||
|
{
|
||||||
|
return Results.NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
var nominationCount = await db.Nominations.CountAsync(item => item.CandidateId == candidateId, cancellationToken);
|
||||||
|
var clipCount = await db.ClipSubmissions.CountAsync(item => item.CandidateId == candidateId, cancellationToken);
|
||||||
|
var voteCount = await db.VoteEntries.CountAsync(item => item.CandidateId == candidateId, cancellationToken);
|
||||||
|
var resultCount = await db.Results.CountAsync(item => item.CandidateId == candidateId, cancellationToken);
|
||||||
|
|
||||||
|
return Results.Ok(new
|
||||||
|
{
|
||||||
|
candidateId,
|
||||||
|
nominationCount,
|
||||||
|
clipCount,
|
||||||
|
voteCount,
|
||||||
|
resultCount,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private static async Task<IResult> DeleteCandidate(
|
private static async Task<IResult> DeleteCandidate(
|
||||||
HttpContext context,
|
HttpContext context,
|
||||||
int candidateId,
|
int candidateId,
|
||||||
@@ -206,6 +234,38 @@ public static partial class AdminSeasonManagementEndpoints
|
|||||||
return Results.NotFound();
|
return Results.NotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var linkedNominations = await db.Nominations
|
||||||
|
.Where(item => item.CandidateId == candidateId)
|
||||||
|
.ToListAsync(context.RequestAborted);
|
||||||
|
if (linkedNominations.Count > 0)
|
||||||
|
{
|
||||||
|
db.Nominations.RemoveRange(linkedNominations);
|
||||||
|
}
|
||||||
|
|
||||||
|
var linkedClips = await db.ClipSubmissions
|
||||||
|
.Where(item => item.CandidateId == candidateId)
|
||||||
|
.ToListAsync(context.RequestAborted);
|
||||||
|
if (linkedClips.Count > 0)
|
||||||
|
{
|
||||||
|
db.ClipSubmissions.RemoveRange(linkedClips);
|
||||||
|
}
|
||||||
|
|
||||||
|
var linkedVoteEntries = await db.VoteEntries
|
||||||
|
.Where(item => item.CandidateId == candidateId)
|
||||||
|
.ToListAsync(context.RequestAborted);
|
||||||
|
if (linkedVoteEntries.Count > 0)
|
||||||
|
{
|
||||||
|
db.VoteEntries.RemoveRange(linkedVoteEntries);
|
||||||
|
}
|
||||||
|
|
||||||
|
var linkedResults = await db.Results
|
||||||
|
.Where(item => item.CandidateId == candidateId)
|
||||||
|
.ToListAsync(context.RequestAborted);
|
||||||
|
if (linkedResults.Count > 0)
|
||||||
|
{
|
||||||
|
db.Results.RemoveRange(linkedResults);
|
||||||
|
}
|
||||||
|
|
||||||
db.Candidates.Remove(candidate);
|
db.Candidates.Remove(candidate);
|
||||||
adminAuditService.AddEntry(
|
adminAuditService.AddEntry(
|
||||||
session.TwitchUserId,
|
session.TwitchUserId,
|
||||||
@@ -213,11 +273,29 @@ public static partial class AdminSeasonManagementEndpoints
|
|||||||
"candidate",
|
"candidate",
|
||||||
candidate.Id.ToString(),
|
candidate.Id.ToString(),
|
||||||
$"Kandidat {candidate.DisplayName} wurde gelöscht.",
|
$"Kandidat {candidate.DisplayName} wurde gelöscht.",
|
||||||
new { candidate.CategoryId, candidate.Platform },
|
new
|
||||||
|
{
|
||||||
|
candidate.CategoryId,
|
||||||
|
candidate.Platform,
|
||||||
|
deletedNominations = linkedNominations.Count,
|
||||||
|
deletedClips = linkedClips.Count,
|
||||||
|
deletedVoteEntries = linkedVoteEntries.Count,
|
||||||
|
deletedResults = linkedResults.Count,
|
||||||
|
},
|
||||||
RequestMetadataReader.Read(context));
|
RequestMetadataReader.Read(context));
|
||||||
|
|
||||||
await db.SaveChangesAsync(context.RequestAborted);
|
try
|
||||||
return Results.Ok(new { deleted = true, candidateId });
|
{
|
||||||
|
await db.SaveChangesAsync(context.RequestAborted);
|
||||||
|
return Results.Ok(new { deleted = true, candidateId });
|
||||||
|
}
|
||||||
|
catch (DbUpdateException)
|
||||||
|
{
|
||||||
|
return Results.BadRequest(new
|
||||||
|
{
|
||||||
|
message = "Kandidat konnte nicht gelöscht werden, weil noch verknüpfte Daten blockieren.",
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string NormalizeCandidateChoice(string? value, string fallback, IReadOnlyCollection<string> allowedValues)
|
private static string NormalizeCandidateChoice(string? value, string fallback, IReadOnlyCollection<string> allowedValues)
|
||||||
|
|||||||
@@ -23,11 +23,11 @@ public static partial class AdminSeasonManagementEndpoints
|
|||||||
.FirstOrDefaultAsync(item => item.Id == 1);
|
.FirstOrDefaultAsync(item => item.Id == 1);
|
||||||
var trackingRules = TrackingRulesSettings.Read(settings);
|
var trackingRules = TrackingRulesSettings.Read(settings);
|
||||||
|
|
||||||
var candidates = await db.Candidates
|
var candidateRows = await db.Candidates
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.Where(item => item.SeasonId == seasonId)
|
.Where(item => item.SeasonId == seasonId)
|
||||||
.OrderBy(item => item.DisplayName)
|
.OrderBy(item => item.DisplayName)
|
||||||
.Select(item => new AdminCandidateItemDto(
|
.Select(item => new AdminCandidateRow(
|
||||||
item.Id,
|
item.Id,
|
||||||
item.CategoryId,
|
item.CategoryId,
|
||||||
item.StreamerIdentityId,
|
item.StreamerIdentityId,
|
||||||
@@ -43,7 +43,7 @@ public static partial class AdminSeasonManagementEndpoints
|
|||||||
item.ClipEmbedStatus))
|
item.ClipEmbedStatus))
|
||||||
.ToArrayAsync();
|
.ToArrayAsync();
|
||||||
|
|
||||||
var candidateCounts = candidates
|
var candidateCounts = candidateRows
|
||||||
.GroupBy(item => item.CategoryId)
|
.GroupBy(item => item.CategoryId)
|
||||||
.ToDictionary(grouping => grouping.Key, grouping => grouping.Count());
|
.ToDictionary(grouping => grouping.Key, grouping => grouping.Count());
|
||||||
|
|
||||||
@@ -76,21 +76,7 @@ public static partial class AdminSeasonManagementEndpoints
|
|||||||
ViewerRangeMin = category.ViewerRangeMin,
|
ViewerRangeMin = category.ViewerRangeMin,
|
||||||
ViewerRangeMax = category.ViewerRangeMax,
|
ViewerRangeMax = category.ViewerRangeMax,
|
||||||
}));
|
}));
|
||||||
var visibleCategoryRows = categoryRows
|
var categories = categoryRows
|
||||||
.Where(category => SeasonSubcategoryTemplateSettings.MatchesTemplate(
|
|
||||||
new Backend.Domain.Category
|
|
||||||
{
|
|
||||||
GroupName = category.GroupName,
|
|
||||||
Name = category.Name,
|
|
||||||
Slug = category.Slug,
|
|
||||||
SortOrder = category.SortOrder,
|
|
||||||
ViewerRangeMin = category.ViewerRangeMin,
|
|
||||||
ViewerRangeMax = category.ViewerRangeMax,
|
|
||||||
},
|
|
||||||
subcategoryTemplateSettings))
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
var categories = visibleCategoryRows
|
|
||||||
.Select(category => new AdminCategoryItemDto(
|
.Select(category => new AdminCategoryItemDto(
|
||||||
category.Id,
|
category.Id,
|
||||||
category.GroupName,
|
category.GroupName,
|
||||||
@@ -217,6 +203,11 @@ public static partial class AdminSeasonManagementEndpoints
|
|||||||
item.CategoryId,
|
item.CategoryId,
|
||||||
item.CandidateId))
|
item.CandidateId))
|
||||||
.ToArrayAsync();
|
.ToArrayAsync();
|
||||||
|
var candidates = BuildCandidateItems(
|
||||||
|
candidateRows,
|
||||||
|
pendingNominationRows,
|
||||||
|
reviewedNominationRows,
|
||||||
|
votingEntryRows);
|
||||||
var votingWorkspace = BuildVotingWorkspace(
|
var votingWorkspace = BuildVotingWorkspace(
|
||||||
categories,
|
categories,
|
||||||
candidates,
|
candidates,
|
||||||
@@ -281,6 +272,21 @@ public static partial class AdminSeasonManagementEndpoints
|
|||||||
int CategoryId,
|
int CategoryId,
|
||||||
int CandidateId);
|
int CandidateId);
|
||||||
|
|
||||||
|
private sealed record AdminCandidateRow(
|
||||||
|
int Id,
|
||||||
|
int CategoryId,
|
||||||
|
int? StreamerIdentityId,
|
||||||
|
string DisplayName,
|
||||||
|
string ChannelSlug,
|
||||||
|
string Platform,
|
||||||
|
int NominationTally,
|
||||||
|
string AcceptanceStatus,
|
||||||
|
string? AcceptanceNote,
|
||||||
|
string? ClipCompilationUrl,
|
||||||
|
string? ClipCompilationTitle,
|
||||||
|
string? ClipCompilationPlatform,
|
||||||
|
string ClipEmbedStatus);
|
||||||
|
|
||||||
private sealed record AdminNominationRow(
|
private sealed record AdminNominationRow(
|
||||||
int Id,
|
int Id,
|
||||||
int? CategoryId,
|
int? CategoryId,
|
||||||
@@ -314,6 +320,79 @@ public static partial class AdminSeasonManagementEndpoints
|
|||||||
string? ReviewedByTwitchId,
|
string? ReviewedByTwitchId,
|
||||||
DateTimeOffset? ReviewedAt);
|
DateTimeOffset? ReviewedAt);
|
||||||
|
|
||||||
|
private static AdminCandidateItemDto[] BuildCandidateItems(
|
||||||
|
AdminCandidateRow[] candidateRows,
|
||||||
|
AdminNominationRow[] pendingNominationRows,
|
||||||
|
AdminNominationRow[] reviewedNominationRows,
|
||||||
|
AdminVotingEntryRow[] votingEntryRows)
|
||||||
|
{
|
||||||
|
var allNominationRows = pendingNominationRows
|
||||||
|
.Concat(reviewedNominationRows)
|
||||||
|
.ToArray();
|
||||||
|
var voteCountByCandidate = votingEntryRows
|
||||||
|
.GroupBy(item => item.CandidateId)
|
||||||
|
.ToDictionary(group => group.Key, group => group.Count());
|
||||||
|
|
||||||
|
return candidateRows
|
||||||
|
.Select(item => new AdminCandidateItemDto(
|
||||||
|
item.Id,
|
||||||
|
item.CategoryId,
|
||||||
|
item.StreamerIdentityId,
|
||||||
|
item.DisplayName,
|
||||||
|
item.ChannelSlug,
|
||||||
|
item.Platform,
|
||||||
|
ResolveCandidateAvgViewers(item, allNominationRows),
|
||||||
|
voteCountByCandidate.GetValueOrDefault(item.Id, 0),
|
||||||
|
item.NominationTally,
|
||||||
|
item.AcceptanceStatus,
|
||||||
|
item.AcceptanceNote,
|
||||||
|
item.ClipCompilationUrl,
|
||||||
|
item.ClipCompilationTitle,
|
||||||
|
item.ClipCompilationPlatform,
|
||||||
|
item.ClipEmbedStatus))
|
||||||
|
.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int? ResolveCandidateAvgViewers(AdminCandidateRow candidate, AdminNominationRow[] nominationRows)
|
||||||
|
{
|
||||||
|
var directMatch = nominationRows
|
||||||
|
.Where(item => item.CandidateId == candidate.Id && item.AvgViewers.HasValue)
|
||||||
|
.OrderByDescending(item => item.TrackerCheckedAt ?? item.ReviewedAt ?? item.CreatedAt)
|
||||||
|
.Select(item => item.AvgViewers)
|
||||||
|
.FirstOrDefault();
|
||||||
|
if (directMatch.HasValue)
|
||||||
|
{
|
||||||
|
return directMatch.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (candidate.StreamerIdentityId.HasValue)
|
||||||
|
{
|
||||||
|
var identityMatch = nominationRows
|
||||||
|
.Where(item => item.StreamerIdentityId == candidate.StreamerIdentityId && item.AvgViewers.HasValue)
|
||||||
|
.OrderByDescending(item => item.TrackerCheckedAt ?? item.ReviewedAt ?? item.CreatedAt)
|
||||||
|
.Select(item => item.AvgViewers)
|
||||||
|
.FirstOrDefault();
|
||||||
|
if (identityMatch.HasValue)
|
||||||
|
{
|
||||||
|
return identityMatch.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var normalizedChannel = candidate.ChannelSlug.Trim().TrimStart('@').ToLowerInvariant();
|
||||||
|
if (string.IsNullOrWhiteSpace(normalizedChannel))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nominationRows
|
||||||
|
.Where(item =>
|
||||||
|
item.AvgViewers.HasValue
|
||||||
|
&& string.Equals(item.ResolvedChannel?.Trim().TrimStart('@'), normalizedChannel, StringComparison.OrdinalIgnoreCase))
|
||||||
|
.OrderByDescending(item => item.TrackerCheckedAt ?? item.ReviewedAt ?? item.CreatedAt)
|
||||||
|
.Select(item => item.AvgViewers)
|
||||||
|
.FirstOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
private static AdminNominationReviewItemDto ToNominationReviewItem(
|
private static AdminNominationReviewItemDto ToNominationReviewItem(
|
||||||
AdminNominationRow item,
|
AdminNominationRow item,
|
||||||
IEnumerable<dynamic> categoryRows,
|
IEnumerable<dynamic> categoryRows,
|
||||||
|
|||||||
@@ -60,6 +60,10 @@ public static partial class AdminSeasonManagementEndpoints
|
|||||||
.AddEndpointFilter((context, next) => AdminEndpointConventions.RequireWritePermission(context, next, Backend.Security.AdminPermissionCatalog.Candidates))
|
.AddEndpointFilter((context, next) => AdminEndpointConventions.RequireWritePermission(context, next, Backend.Security.AdminPermissionCatalog.Candidates))
|
||||||
.WithName("UpdateAdminCandidate")
|
.WithName("UpdateAdminCandidate")
|
||||||
.WithOpenApi();
|
.WithOpenApi();
|
||||||
|
group.MapGet("/candidates/{candidateId:int}/delete-preview", GetCandidateDeletePreview)
|
||||||
|
.AddEndpointFilter((context, next) => AdminEndpointConventions.RequireWritePermission(context, next, Backend.Security.AdminPermissionCatalog.Candidates))
|
||||||
|
.WithName("GetAdminCandidateDeletePreview")
|
||||||
|
.WithOpenApi();
|
||||||
group.MapDelete("/candidates/{candidateId:int}", DeleteCandidate)
|
group.MapDelete("/candidates/{candidateId:int}", DeleteCandidate)
|
||||||
.AddEndpointFilter((context, next) => AdminEndpointConventions.RequireWritePermission(context, next, Backend.Security.AdminPermissionCatalog.Candidates))
|
.AddEndpointFilter((context, next) => AdminEndpointConventions.RequireWritePermission(context, next, Backend.Security.AdminPermissionCatalog.Candidates))
|
||||||
.WithName("DeleteAdminCandidate")
|
.WithName("DeleteAdminCandidate")
|
||||||
|
|||||||
@@ -14,6 +14,15 @@ public static class AdminSiteSettingsEndpoints
|
|||||||
private const string FallbackMaintenanceTitle = "Sternenpause";
|
private const string FallbackMaintenanceTitle = "Sternenpause";
|
||||||
private const string FallbackMaintenanceMessage = "Die Award-Galaxie wird gerade liebevoll poliert. Schau gleich wieder vorbei.";
|
private const string FallbackMaintenanceMessage = "Die Award-Galaxie wird gerade liebevoll poliert. Schau gleich wieder vorbei.";
|
||||||
private const string FallbackClipSubmissionDisabledMessage = "Clip-Einreichungen sind aktuell geschlossen.";
|
private const string FallbackClipSubmissionDisabledMessage = "Clip-Einreichungen sind aktuell geschlossen.";
|
||||||
|
private const string DefaultHostImageUrl = "/assets/amaterasu2sei_2.png";
|
||||||
|
private const int MaxHostImageBytes = 8 * 1024 * 1024;
|
||||||
|
|
||||||
|
private static readonly IReadOnlyDictionary<string, string> AllowedHostImageContentTypes = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
["image/png"] = ".png",
|
||||||
|
["image/jpeg"] = ".jpg",
|
||||||
|
["image/webp"] = ".webp",
|
||||||
|
};
|
||||||
|
|
||||||
public static RouteGroupBuilder MapAdminSiteSettingsEndpoints(this RouteGroupBuilder group)
|
public static RouteGroupBuilder MapAdminSiteSettingsEndpoints(this RouteGroupBuilder group)
|
||||||
{
|
{
|
||||||
@@ -25,6 +34,10 @@ public static class AdminSiteSettingsEndpoints
|
|||||||
.AddEndpointFilter((context, next) => AdminEndpointConventions.RequireWritePermission(context, next, AdminPermissionCatalog.Content))
|
.AddEndpointFilter((context, next) => AdminEndpointConventions.RequireWritePermission(context, next, AdminPermissionCatalog.Content))
|
||||||
.WithName("UpdateAdminSiteSettings")
|
.WithName("UpdateAdminSiteSettings")
|
||||||
.WithOpenApi();
|
.WithOpenApi();
|
||||||
|
group.MapPost("/site-settings/host-image", UploadHostImage)
|
||||||
|
.AddEndpointFilter((context, next) => AdminEndpointConventions.RequireWritePermission(context, next, AdminPermissionCatalog.Content))
|
||||||
|
.WithName("UploadAdminHostImage")
|
||||||
|
.WithOpenApi();
|
||||||
group.MapGet("/operational-settings", GetOperationalSettings)
|
group.MapGet("/operational-settings", GetOperationalSettings)
|
||||||
.AddEndpointFilter((context, next) => AdminEndpointConventions.RequirePermission(context, next, AdminPermissionCatalog.Settings))
|
.AddEndpointFilter((context, next) => AdminEndpointConventions.RequirePermission(context, next, AdminPermissionCatalog.Settings))
|
||||||
.WithName("GetAdminOperationalSettings")
|
.WithName("GetAdminOperationalSettings")
|
||||||
@@ -68,9 +81,175 @@ public static class AdminSiteSettingsEndpoints
|
|||||||
return Results.NotFound();
|
return Results.NotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Results.Ok(new AdminSiteSettingsResponse(
|
return Results.Ok(MapSiteSettingsResponse(settings));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<IResult> UploadHostImage(
|
||||||
|
HttpContext context,
|
||||||
|
AwardsDbContext db,
|
||||||
|
IAdminAuditService adminAuditService)
|
||||||
|
{
|
||||||
|
if (!context.Request.HasFormContentType)
|
||||||
|
{
|
||||||
|
return Results.BadRequest(new { message = "Bitte ein Bild als Formular-Upload senden." });
|
||||||
|
}
|
||||||
|
|
||||||
|
var form = await context.Request.ReadFormAsync(context.RequestAborted);
|
||||||
|
var file = form.Files.GetFile("file") ?? form.Files.FirstOrDefault();
|
||||||
|
if (file is null || file.Length == 0)
|
||||||
|
{
|
||||||
|
return Results.BadRequest(new { message = "Bitte ein Hostbild auswählen." });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file.Length > MaxHostImageBytes)
|
||||||
|
{
|
||||||
|
return Results.BadRequest(new { message = "Hostbild ist zu groß. Maximal erlaubt sind 8 MB." });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!AllowedHostImageContentTypes.TryGetValue(file.ContentType, out var expectedExtension))
|
||||||
|
{
|
||||||
|
return Results.BadRequest(new { message = "Bitte PNG, JPG oder WebP hochladen." });
|
||||||
|
}
|
||||||
|
|
||||||
|
var extension = Path.GetExtension(file.FileName);
|
||||||
|
if (!string.IsNullOrWhiteSpace(extension)
|
||||||
|
&& !string.Equals(extension, expectedExtension, StringComparison.OrdinalIgnoreCase)
|
||||||
|
&& !(string.Equals(file.ContentType, "image/jpeg", StringComparison.OrdinalIgnoreCase)
|
||||||
|
&& string.Equals(extension, ".jpeg", StringComparison.OrdinalIgnoreCase)))
|
||||||
|
{
|
||||||
|
return Results.BadRequest(new { message = "Dateiendung und Bildtyp passen nicht zusammen." });
|
||||||
|
}
|
||||||
|
|
||||||
|
var settings = await db.SiteSettings.FirstOrDefaultAsync(item => item.Id == 1);
|
||||||
|
if (settings is null)
|
||||||
|
{
|
||||||
|
return Results.NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
await using var stream = file.OpenReadStream();
|
||||||
|
using var memory = new MemoryStream((int)file.Length);
|
||||||
|
await stream.CopyToAsync(memory, context.RequestAborted);
|
||||||
|
|
||||||
|
settings.HostImageData = memory.ToArray();
|
||||||
|
settings.HostImageContentType = file.ContentType;
|
||||||
|
settings.HostImageUpdatedAt = DateTimeOffset.UtcNow;
|
||||||
|
|
||||||
|
var session = AdminEndpointConventions.CurrentSession(context);
|
||||||
|
adminAuditService.AddEntry(
|
||||||
|
session.TwitchUserId,
|
||||||
|
"site-settings.host-image.upload",
|
||||||
|
"site-settings",
|
||||||
|
settings.Id.ToString(),
|
||||||
|
"Landingpage-Hostbild wurde aktualisiert.",
|
||||||
|
new
|
||||||
|
{
|
||||||
|
fileName = file.FileName,
|
||||||
|
fileSize = file.Length,
|
||||||
|
file.ContentType,
|
||||||
|
},
|
||||||
|
RequestMetadataReader.Read(context));
|
||||||
|
|
||||||
|
await db.SaveChangesAsync(context.RequestAborted);
|
||||||
|
return Results.Ok(MapSiteSettingsResponse(settings));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<IResult> UpdateSiteSettings(
|
||||||
|
HttpContext context,
|
||||||
|
UpdateSiteSettingsRequest? request,
|
||||||
|
AwardsDbContext db,
|
||||||
|
IAdminAuditService adminAuditService)
|
||||||
|
{
|
||||||
|
var requestValidationError = ValidateSiteSettingsRequest(request);
|
||||||
|
if (requestValidationError is not null)
|
||||||
|
{
|
||||||
|
return requestValidationError;
|
||||||
|
}
|
||||||
|
|
||||||
|
var validatedRequest = request!;
|
||||||
|
var session = AdminEndpointConventions.CurrentSession(context);
|
||||||
|
|
||||||
|
var settings = await db.SiteSettings.FirstOrDefaultAsync(item => item.Id == 1);
|
||||||
|
if (settings is null)
|
||||||
|
{
|
||||||
|
return Results.NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
var urlValidationError = NormalizeSiteSettingsUrls(validatedRequest, out var normalizedUrls, out var socialLinks);
|
||||||
|
if (urlValidationError is not null)
|
||||||
|
{
|
||||||
|
return urlValidationError;
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.HostDisplayName = validatedRequest.HostDisplayName.Trim();
|
||||||
|
settings.HostTagline = validatedRequest.HostTagline.Trim();
|
||||||
|
settings.HostArtistName = validatedRequest.HostArtistName.Trim();
|
||||||
|
settings.NewsletterUrl = normalizedUrls.NewsletterUrl;
|
||||||
|
settings.ShareXUrl = normalizedUrls.ShareXUrl;
|
||||||
|
settings.ShareDiscordUrl = normalizedUrls.ShareDiscordUrl;
|
||||||
|
settings.PrivacyEmail = validatedRequest.PrivacyEmail.Trim();
|
||||||
|
var trimmedPrivacyContent = validatedRequest.PrivacyPolicyContent.Trim();
|
||||||
|
var privacyChanged = !string.Equals(settings.PrivacyPolicyContent, trimmedPrivacyContent, StringComparison.Ordinal);
|
||||||
|
settings.PrivacyPolicyContent = trimmedPrivacyContent;
|
||||||
|
if (privacyChanged)
|
||||||
|
{
|
||||||
|
settings.PrivacyPolicyUpdatedBy = session.DisplayName.Trim();
|
||||||
|
settings.PrivacyPolicyUpdatedAt = DateTimeOffset.UtcNow;
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.ImprintUrl = normalizedUrls.ImprintUrl;
|
||||||
|
settings.ImprintContent = validatedRequest.ImprintContent.Trim();
|
||||||
|
settings.ContactUrl = normalizedUrls.ContactUrl;
|
||||||
|
settings.ContactContent = validatedRequest.ContactContent.Trim();
|
||||||
|
settings.SponsorsUrl = normalizedUrls.SponsorsUrl;
|
||||||
|
settings.SponsorsContent = validatedRequest.SponsorsContent.Trim();
|
||||||
|
settings.ShowactsUrl = normalizedUrls.ShowactsUrl;
|
||||||
|
settings.ShowactsContent = validatedRequest.ShowactsContent.Trim();
|
||||||
|
settings.StreamBannerEyebrow = SeasonMappings.NormalizePlainTextContent(validatedRequest.StreamBannerEyebrow);
|
||||||
|
settings.StreamBannerTitle = SeasonMappings.NormalizePlainTextContent(validatedRequest.StreamBannerTitle);
|
||||||
|
settings.StreamBannerText = SeasonMappings.NormalizePlainTextContent(validatedRequest.StreamBannerText);
|
||||||
|
settings.StreamBannerLiveButtonLabel = SeasonMappings.NormalizePlainTextContent(validatedRequest.StreamBannerLiveButtonLabel);
|
||||||
|
settings.StreamBannerLiveButtonUrl = normalizedUrls.StreamBannerLiveButtonUrl;
|
||||||
|
settings.StreamBannerLockedButtonLabel = SeasonMappings.NormalizePlainTextContent(validatedRequest.StreamBannerLockedButtonLabel);
|
||||||
|
settings.StreamBannerUseCompletedContent = validatedRequest.StreamBannerUseCompletedContent;
|
||||||
|
settings.StreamBannerCompletedEyebrow = SeasonMappings.NormalizePlainTextContent(validatedRequest.StreamBannerCompletedEyebrow);
|
||||||
|
settings.StreamBannerCompletedTitle = SeasonMappings.NormalizePlainTextContent(validatedRequest.StreamBannerCompletedTitle);
|
||||||
|
settings.StreamBannerCompletedText = SeasonMappings.NormalizePlainTextContent(validatedRequest.StreamBannerCompletedText);
|
||||||
|
settings.StreamBannerCompletedButtonLabel = SeasonMappings.NormalizePlainTextContent(validatedRequest.StreamBannerCompletedButtonLabel);
|
||||||
|
settings.StreamBannerCompletedButtonUrl = normalizedUrls.StreamBannerCompletedButtonUrl;
|
||||||
|
settings.AwardsSectionTitle = SeasonMappings.NormalizePlainTextContent(validatedRequest.AwardsSectionTitle);
|
||||||
|
settings.AwardsSectionDescription = SeasonMappings.NormalizePlainTextContent(validatedRequest.AwardsSectionDescription);
|
||||||
|
settings.SubcategoriesSectionTitle = SeasonMappings.NormalizePlainTextContent(validatedRequest.SubcategoriesSectionTitle);
|
||||||
|
settings.SubcategoriesSectionDescription = SeasonMappings.NormalizePlainTextContent(validatedRequest.SubcategoriesSectionDescription);
|
||||||
|
settings.SocialLinksJson = JsonSerializer.Serialize(socialLinks);
|
||||||
|
settings.FaqJson = JsonSerializer.Serialize(validatedRequest.Faq);
|
||||||
|
settings.ShowactFormSchemaJson = validatedRequest.ShowactFormSchemaJson ?? "[]";
|
||||||
|
|
||||||
|
adminAuditService.AddEntry(
|
||||||
|
session.TwitchUserId,
|
||||||
|
"site-settings.update",
|
||||||
|
"site-settings",
|
||||||
|
settings.Id.ToString(),
|
||||||
|
"Public Site Settings wurden aktualisiert.",
|
||||||
|
new
|
||||||
|
{
|
||||||
|
settings.HostDisplayName,
|
||||||
|
privacyChanged,
|
||||||
|
socialLinkCount = socialLinks.Length,
|
||||||
|
faqCount = validatedRequest.Faq.Length,
|
||||||
|
},
|
||||||
|
RequestMetadataReader.Read(context));
|
||||||
|
|
||||||
|
await db.SaveChangesAsync(context.RequestAborted);
|
||||||
|
return Results.Ok(new { saved = true });
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AdminSiteSettingsResponse MapSiteSettingsResponse(SiteSettings settings)
|
||||||
|
{
|
||||||
|
return new AdminSiteSettingsResponse(
|
||||||
settings.HostDisplayName,
|
settings.HostDisplayName,
|
||||||
settings.HostTagline,
|
settings.HostTagline,
|
||||||
|
settings.HostArtistName,
|
||||||
|
BuildHostImageUrl(settings),
|
||||||
settings.NewsletterUrl,
|
settings.NewsletterUrl,
|
||||||
settings.ShareXUrl,
|
settings.ShareXUrl,
|
||||||
settings.ShareDiscordUrl,
|
settings.ShareDiscordUrl,
|
||||||
@@ -104,89 +283,19 @@ public static class AdminSiteSettingsEndpoints
|
|||||||
SeasonMappings.NormalizePlainTextContent(settings.SubcategoriesSectionDescription),
|
SeasonMappings.NormalizePlainTextContent(settings.SubcategoriesSectionDescription),
|
||||||
SeasonMappings.ReadSocialLinks(settings),
|
SeasonMappings.ReadSocialLinks(settings),
|
||||||
SeasonMappings.ReadFaqItems(settings),
|
SeasonMappings.ReadFaqItems(settings),
|
||||||
settings.ShowactFormSchemaJson ?? "[]"));
|
settings.ShowactFormSchemaJson ?? "[]");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<IResult> UpdateSiteSettings(
|
internal static string BuildHostImageUrl(SiteSettings settings)
|
||||||
HttpContext context,
|
|
||||||
UpdateSiteSettingsRequest request,
|
|
||||||
AwardsDbContext db,
|
|
||||||
IAdminAuditService adminAuditService)
|
|
||||||
{
|
{
|
||||||
var session = AdminEndpointConventions.CurrentSession(context);
|
if (settings.HostImageData is not { Length: > 0 })
|
||||||
|
|
||||||
var settings = await db.SiteSettings.FirstOrDefaultAsync(item => item.Id == 1);
|
|
||||||
if (settings is null)
|
|
||||||
{
|
{
|
||||||
return Results.NotFound();
|
return DefaultHostImageUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
var urlValidationError = NormalizeSiteSettingsUrls(request, out var normalizedUrls, out var socialLinks);
|
var version = settings.HostImageUpdatedAt?.ToUnixTimeSeconds().ToString(System.Globalization.CultureInfo.InvariantCulture)
|
||||||
if (urlValidationError is not null)
|
?? settings.HostImageData.Length.ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||||
{
|
return $"/api/public/host-image?v={version}";
|
||||||
return urlValidationError;
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.HostDisplayName = request.HostDisplayName.Trim();
|
|
||||||
settings.HostTagline = request.HostTagline.Trim();
|
|
||||||
settings.NewsletterUrl = normalizedUrls.NewsletterUrl;
|
|
||||||
settings.ShareXUrl = normalizedUrls.ShareXUrl;
|
|
||||||
settings.ShareDiscordUrl = normalizedUrls.ShareDiscordUrl;
|
|
||||||
settings.PrivacyEmail = request.PrivacyEmail.Trim();
|
|
||||||
var trimmedPrivacyContent = request.PrivacyPolicyContent.Trim();
|
|
||||||
var privacyChanged = !string.Equals(settings.PrivacyPolicyContent, trimmedPrivacyContent, StringComparison.Ordinal);
|
|
||||||
settings.PrivacyPolicyContent = trimmedPrivacyContent;
|
|
||||||
if (privacyChanged)
|
|
||||||
{
|
|
||||||
settings.PrivacyPolicyUpdatedBy = session.DisplayName.Trim();
|
|
||||||
settings.PrivacyPolicyUpdatedAt = DateTimeOffset.UtcNow;
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.ImprintUrl = normalizedUrls.ImprintUrl;
|
|
||||||
settings.ImprintContent = request.ImprintContent.Trim();
|
|
||||||
settings.ContactUrl = normalizedUrls.ContactUrl;
|
|
||||||
settings.ContactContent = request.ContactContent.Trim();
|
|
||||||
settings.SponsorsUrl = normalizedUrls.SponsorsUrl;
|
|
||||||
settings.SponsorsContent = request.SponsorsContent.Trim();
|
|
||||||
settings.ShowactsUrl = normalizedUrls.ShowactsUrl;
|
|
||||||
settings.ShowactsContent = request.ShowactsContent.Trim();
|
|
||||||
settings.StreamBannerEyebrow = SeasonMappings.NormalizePlainTextContent(request.StreamBannerEyebrow);
|
|
||||||
settings.StreamBannerTitle = SeasonMappings.NormalizePlainTextContent(request.StreamBannerTitle);
|
|
||||||
settings.StreamBannerText = SeasonMappings.NormalizePlainTextContent(request.StreamBannerText);
|
|
||||||
settings.StreamBannerLiveButtonLabel = SeasonMappings.NormalizePlainTextContent(request.StreamBannerLiveButtonLabel);
|
|
||||||
settings.StreamBannerLiveButtonUrl = normalizedUrls.StreamBannerLiveButtonUrl;
|
|
||||||
settings.StreamBannerLockedButtonLabel = SeasonMappings.NormalizePlainTextContent(request.StreamBannerLockedButtonLabel);
|
|
||||||
settings.StreamBannerUseCompletedContent = request.StreamBannerUseCompletedContent;
|
|
||||||
settings.StreamBannerCompletedEyebrow = SeasonMappings.NormalizePlainTextContent(request.StreamBannerCompletedEyebrow);
|
|
||||||
settings.StreamBannerCompletedTitle = SeasonMappings.NormalizePlainTextContent(request.StreamBannerCompletedTitle);
|
|
||||||
settings.StreamBannerCompletedText = SeasonMappings.NormalizePlainTextContent(request.StreamBannerCompletedText);
|
|
||||||
settings.StreamBannerCompletedButtonLabel = SeasonMappings.NormalizePlainTextContent(request.StreamBannerCompletedButtonLabel);
|
|
||||||
settings.StreamBannerCompletedButtonUrl = normalizedUrls.StreamBannerCompletedButtonUrl;
|
|
||||||
settings.AwardsSectionTitle = SeasonMappings.NormalizePlainTextContent(request.AwardsSectionTitle);
|
|
||||||
settings.AwardsSectionDescription = SeasonMappings.NormalizePlainTextContent(request.AwardsSectionDescription);
|
|
||||||
settings.SubcategoriesSectionTitle = SeasonMappings.NormalizePlainTextContent(request.SubcategoriesSectionTitle);
|
|
||||||
settings.SubcategoriesSectionDescription = SeasonMappings.NormalizePlainTextContent(request.SubcategoriesSectionDescription);
|
|
||||||
settings.SocialLinksJson = JsonSerializer.Serialize(socialLinks);
|
|
||||||
settings.FaqJson = JsonSerializer.Serialize(request.Faq ?? []);
|
|
||||||
settings.ShowactFormSchemaJson = request.ShowactFormSchemaJson ?? "[]";
|
|
||||||
|
|
||||||
adminAuditService.AddEntry(
|
|
||||||
session.TwitchUserId,
|
|
||||||
"site-settings.update",
|
|
||||||
"site-settings",
|
|
||||||
settings.Id.ToString(),
|
|
||||||
"Public Site Settings wurden aktualisiert.",
|
|
||||||
new
|
|
||||||
{
|
|
||||||
settings.HostDisplayName,
|
|
||||||
privacyChanged,
|
|
||||||
socialLinkCount = socialLinks.Length,
|
|
||||||
faqCount = request.Faq?.Length ?? 0,
|
|
||||||
},
|
|
||||||
RequestMetadataReader.Read(context));
|
|
||||||
|
|
||||||
await db.SaveChangesAsync(context.RequestAborted);
|
|
||||||
return Results.Ok(new { saved = true });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IResult? NormalizeSiteSettingsUrls(
|
private static IResult? NormalizeSiteSettingsUrls(
|
||||||
@@ -238,6 +347,58 @@ public static class AdminSiteSettingsEndpoints
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static IResult? ValidateSiteSettingsRequest(UpdateSiteSettingsRequest? request)
|
||||||
|
{
|
||||||
|
if (request is null)
|
||||||
|
{
|
||||||
|
return Results.BadRequest(new { message = "Public Site Settings brauchen einen Request-Body." });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.SocialLinks is null || request.Faq is null)
|
||||||
|
{
|
||||||
|
return Results.BadRequest(new { message = "Public Site Settings brauchen Social Links und FAQ im Request-Body." });
|
||||||
|
}
|
||||||
|
|
||||||
|
string?[] requiredTextFields =
|
||||||
|
[
|
||||||
|
request.HostDisplayName,
|
||||||
|
request.HostTagline,
|
||||||
|
request.HostArtistName,
|
||||||
|
request.NewsletterUrl,
|
||||||
|
request.ShareXUrl,
|
||||||
|
request.ShareDiscordUrl,
|
||||||
|
request.PrivacyEmail,
|
||||||
|
request.PrivacyPolicyContent,
|
||||||
|
request.ImprintUrl,
|
||||||
|
request.ImprintContent,
|
||||||
|
request.ContactUrl,
|
||||||
|
request.ContactContent,
|
||||||
|
request.SponsorsUrl,
|
||||||
|
request.SponsorsContent,
|
||||||
|
request.ShowactsUrl,
|
||||||
|
request.ShowactsContent,
|
||||||
|
request.StreamBannerEyebrow,
|
||||||
|
request.StreamBannerTitle,
|
||||||
|
request.StreamBannerText,
|
||||||
|
request.StreamBannerLiveButtonLabel,
|
||||||
|
request.StreamBannerLiveButtonUrl,
|
||||||
|
request.StreamBannerLockedButtonLabel,
|
||||||
|
request.StreamBannerCompletedEyebrow,
|
||||||
|
request.StreamBannerCompletedTitle,
|
||||||
|
request.StreamBannerCompletedText,
|
||||||
|
request.StreamBannerCompletedButtonLabel,
|
||||||
|
request.StreamBannerCompletedButtonUrl,
|
||||||
|
request.AwardsSectionTitle,
|
||||||
|
request.AwardsSectionDescription,
|
||||||
|
request.SubcategoriesSectionTitle,
|
||||||
|
request.SubcategoriesSectionDescription,
|
||||||
|
];
|
||||||
|
|
||||||
|
return requiredTextFields.Any(field => field is null)
|
||||||
|
? Results.BadRequest(new { message = "Public Site Settings brauchen alle Pflichtfelder im Request-Body." })
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
private static bool TryNormalizePublicUrl(string? value, string fieldName, out string normalizedUrl, out string errorMessage)
|
private static bool TryNormalizePublicUrl(string? value, string fieldName, out string normalizedUrl, out string errorMessage)
|
||||||
{
|
{
|
||||||
normalizedUrl = (value ?? string.Empty).Trim();
|
normalizedUrl = (value ?? string.Empty).Trim();
|
||||||
|
|||||||
@@ -15,16 +15,21 @@ public static partial class AuthEndpoints
|
|||||||
|
|
||||||
private static async Task<IResult> TeamLogin(
|
private static async Task<IResult> TeamLogin(
|
||||||
HttpContext context,
|
HttpContext context,
|
||||||
TeamLoginRequest request,
|
TeamLoginRequest? request,
|
||||||
AwardsDbContext db,
|
AwardsDbContext db,
|
||||||
IUserSessionService userSessionService)
|
IUserSessionService userSessionService)
|
||||||
{
|
{
|
||||||
var login = NormalizeTeamLogin(request.Login);
|
if (request is null)
|
||||||
|
{
|
||||||
|
return Results.BadRequest(new { message = "Login und Passwort sind erforderlich." });
|
||||||
|
}
|
||||||
|
|
||||||
|
var login = NormalizeTeamLogin(request.Login ?? string.Empty);
|
||||||
var password = request.Password ?? string.Empty;
|
var password = request.Password ?? string.Empty;
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(login) || string.IsNullOrWhiteSpace(password))
|
if (string.IsNullOrWhiteSpace(login) || string.IsNullOrWhiteSpace(password))
|
||||||
{
|
{
|
||||||
return Results.Unauthorized();
|
return Results.BadRequest(new { message = "Login und Passwort sind erforderlich." });
|
||||||
}
|
}
|
||||||
|
|
||||||
var member = await db.TeamMembers.FirstOrDefaultAsync(item => item.Login == login, context.RequestAborted);
|
var member = await db.TeamMembers.FirstOrDefaultAsync(item => item.Login == login, context.RequestAborted);
|
||||||
@@ -120,8 +125,8 @@ public static partial class AuthEndpoints
|
|||||||
: await db.TeamMembers.FirstOrDefaultAsync(item => item.BoundTwitchUserId == twitchUserId, cancellationToken);
|
: await db.TeamMembers.FirstOrDefaultAsync(item => item.BoundTwitchUserId == twitchUserId, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string NormalizeTeamLogin(string value) =>
|
private static string NormalizeTeamLogin(string? value) =>
|
||||||
value.Trim().TrimStart('@').ToLowerInvariant();
|
(value ?? string.Empty).Trim().TrimStart('@').ToLowerInvariant();
|
||||||
|
|
||||||
private static string NormalizeTwitchUserId(string? value) =>
|
private static string NormalizeTwitchUserId(string? value) =>
|
||||||
(value ?? string.Empty).Trim().TrimStart('@').ToLowerInvariant();
|
(value ?? string.Empty).Trim().TrimStart('@').ToLowerInvariant();
|
||||||
|
|||||||
@@ -11,13 +11,20 @@ public static partial class PublicEndpoints
|
|||||||
{
|
{
|
||||||
private static async Task<IResult> CreateClip(
|
private static async Task<IResult> CreateClip(
|
||||||
HttpContext context,
|
HttpContext context,
|
||||||
CreateClipRequest request,
|
CreateClipRequest? request,
|
||||||
AwardsDbContext db,
|
AwardsDbContext db,
|
||||||
IUserSessionService userSessionService,
|
IUserSessionService userSessionService,
|
||||||
IRiskFlagService riskFlagService,
|
IRiskFlagService riskFlagService,
|
||||||
IRiskRuleService riskRuleService)
|
IRiskRuleService riskRuleService)
|
||||||
{
|
{
|
||||||
if (!TryNormalizeExternalUrl(request.ClipUrl, out var clipUrl))
|
var validationError = ValidateCreateClipRequest(request);
|
||||||
|
if (validationError is not null)
|
||||||
|
{
|
||||||
|
return validationError;
|
||||||
|
}
|
||||||
|
|
||||||
|
var validatedRequest = request!;
|
||||||
|
if (!TryNormalizeExternalUrl(validatedRequest.ClipUrl, out var clipUrl))
|
||||||
{
|
{
|
||||||
return Results.BadRequest(new { message = "A valid http(s) clip link is required." });
|
return Results.BadRequest(new { message = "A valid http(s) clip link is required." });
|
||||||
}
|
}
|
||||||
@@ -44,7 +51,7 @@ public static partial class PublicEndpoints
|
|||||||
return Results.BadRequest(new { message });
|
return Results.BadRequest(new { message });
|
||||||
}
|
}
|
||||||
|
|
||||||
var season = await db.Seasons.FirstOrDefaultAsync(item => item.Year == request.Year);
|
var season = await db.Seasons.FirstOrDefaultAsync(item => item.Year == validatedRequest.Year);
|
||||||
var clipSeasonResolution = EnsurePublicWriteSeason(season, "nomination");
|
var clipSeasonResolution = EnsurePublicWriteSeason(season, "nomination");
|
||||||
if (clipSeasonResolution.Result is not null)
|
if (clipSeasonResolution.Result is not null)
|
||||||
{
|
{
|
||||||
@@ -53,26 +60,26 @@ public static partial class PublicEndpoints
|
|||||||
|
|
||||||
season = clipSeasonResolution.Season!;
|
season = clipSeasonResolution.Season!;
|
||||||
|
|
||||||
var selectedCandidate = request.CandidateId is int candidateId
|
var selectedCandidate = validatedRequest.CandidateId is int candidateId
|
||||||
? await db.Candidates
|
? await db.Candidates
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.FirstOrDefaultAsync(item => item.Id == candidateId && item.SeasonId == season.Id)
|
.FirstOrDefaultAsync(item => item.Id == candidateId && item.SeasonId == season.Id)
|
||||||
: null;
|
: null;
|
||||||
if (request.CandidateId is not null && selectedCandidate is null)
|
if (validatedRequest.CandidateId is not null && selectedCandidate is null)
|
||||||
{
|
{
|
||||||
return Results.BadRequest(new { message = "The selected candidate does not exist for this season." });
|
return Results.BadRequest(new { message = "The selected candidate does not exist for this season." });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.CategoryId is int requestedCategoryId
|
if (validatedRequest.CategoryId is int requestedCategoryId
|
||||||
&& selectedCandidate is not null
|
&& selectedCandidate is not null
|
||||||
&& selectedCandidate.CategoryId != requestedCategoryId)
|
&& selectedCandidate.CategoryId != requestedCategoryId)
|
||||||
{
|
{
|
||||||
return Results.BadRequest(new { message = "The selected candidate does not belong to the selected category." });
|
return Results.BadRequest(new { message = "The selected candidate does not belong to the selected category." });
|
||||||
}
|
}
|
||||||
|
|
||||||
var resolvedCategoryId = request.CategoryId ?? selectedCandidate?.CategoryId;
|
var resolvedCategoryId = validatedRequest.CategoryId ?? selectedCandidate?.CategoryId;
|
||||||
var normalizedTitle = request.Title?.Trim() ?? string.Empty;
|
var normalizedTitle = validatedRequest.Title?.Trim() ?? string.Empty;
|
||||||
var submittedCreator = request.Creator?.Trim();
|
var submittedCreator = validatedRequest.Creator?.Trim();
|
||||||
var normalizedCreator = string.IsNullOrWhiteSpace(submittedCreator)
|
var normalizedCreator = string.IsNullOrWhiteSpace(submittedCreator)
|
||||||
? selectedCandidate?.DisplayName ?? string.Empty
|
? selectedCandidate?.DisplayName ?? string.Empty
|
||||||
: submittedCreator;
|
: submittedCreator;
|
||||||
@@ -96,7 +103,7 @@ public static partial class PublicEndpoints
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var submitterIdResult = await ResolveSubmitterIdAsync(context, request.TwitchUserId, userSessionService);
|
var submitterIdResult = await ResolveSubmitterIdAsync(context, validatedRequest.TwitchUserId, userSessionService);
|
||||||
if (submitterIdResult.Result is not null)
|
if (submitterIdResult.Result is not null)
|
||||||
{
|
{
|
||||||
return submitterIdResult.Result;
|
return submitterIdResult.Result;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Backend.Common;
|
using Backend.Common;
|
||||||
|
using Backend.Contracts;
|
||||||
using Backend.Domain;
|
using Backend.Domain;
|
||||||
using Backend.Services;
|
using Backend.Services;
|
||||||
using Microsoft.AspNetCore.Http.Extensions;
|
using Microsoft.AspNetCore.Http.Extensions;
|
||||||
@@ -205,4 +206,74 @@ public static partial class PublicEndpoints
|
|||||||
"show" => "show",
|
"show" => "show",
|
||||||
_ => "current",
|
_ => "current",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static IResult? ValidateCreateNominationRequest(CreateNominationRequest? request)
|
||||||
|
{
|
||||||
|
if (request is null)
|
||||||
|
{
|
||||||
|
return Results.BadRequest(new { message = "A nomination request body is required." });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.Year <= 0)
|
||||||
|
{
|
||||||
|
return Results.BadRequest(new { message = "A valid season year is required." });
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((request.Nominations?.Length ?? 0) == 0 && (request.Nominees?.Length ?? 0) == 0)
|
||||||
|
{
|
||||||
|
return Results.BadRequest(new { message = "A nomination request must include at least one stream link." });
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IResult? ValidateCreateVoteRequest(CreateVoteRequest? request)
|
||||||
|
{
|
||||||
|
if (request is null)
|
||||||
|
{
|
||||||
|
return Results.BadRequest(new { message = "A vote request body is required." });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.SeasonId <= 0)
|
||||||
|
{
|
||||||
|
return Results.BadRequest(new { message = "A valid season id is required." });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.Entries is null || request.Entries.Length == 0)
|
||||||
|
{
|
||||||
|
return Results.BadRequest(new { message = "At least one vote entry is required." });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.Entries.Any(item => item.CategoryId <= 0 || item.CandidateId <= 0))
|
||||||
|
{
|
||||||
|
return Results.BadRequest(new { message = "Each vote entry requires a valid category and candidate." });
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IResult? ValidateCreateClipRequest(CreateClipRequest? request)
|
||||||
|
{
|
||||||
|
if (request is null)
|
||||||
|
{
|
||||||
|
return Results.BadRequest(new { message = "A clip request body is required." });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.Year <= 0)
|
||||||
|
{
|
||||||
|
return Results.BadRequest(new { message = "A valid season year is required." });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.CategoryId is <= 0 || request.CandidateId is <= 0)
|
||||||
|
{
|
||||||
|
return Results.BadRequest(new { message = "Category and candidate ids must be positive when provided." });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(request.ClipUrl))
|
||||||
|
{
|
||||||
|
return Results.BadRequest(new { message = "A valid http(s) clip link is required." });
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ public static partial class PublicEndpoints
|
|||||||
.WithName("GetSiteStatus")
|
.WithName("GetSiteStatus")
|
||||||
.WithOpenApi();
|
.WithOpenApi();
|
||||||
|
|
||||||
|
group.MapGet("/host-image", GetHostImage)
|
||||||
|
.WithName("GetPublicHostImage")
|
||||||
|
.WithOpenApi();
|
||||||
|
|
||||||
group.MapGet("/seasons/{year:int}/categories", GetSeasonCategories)
|
group.MapGet("/seasons/{year:int}/categories", GetSeasonCategories)
|
||||||
.WithName("GetSeasonCategories")
|
.WithName("GetSeasonCategories")
|
||||||
.WithOpenApi();
|
.WithOpenApi();
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
using Backend.Data;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace Backend.Endpoints;
|
||||||
|
|
||||||
|
public static partial class PublicEndpoints
|
||||||
|
{
|
||||||
|
private static async Task<IResult> GetHostImage(AwardsDbContext db)
|
||||||
|
{
|
||||||
|
var settings = await db.SiteSettings
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(item => item.Id == 1)
|
||||||
|
.Select(item => new
|
||||||
|
{
|
||||||
|
item.HostImageData,
|
||||||
|
item.HostImageContentType,
|
||||||
|
item.HostImageUpdatedAt,
|
||||||
|
})
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
if (settings?.HostImageData is not { Length: > 0 } imageData
|
||||||
|
|| string.IsNullOrWhiteSpace(settings.HostImageContentType))
|
||||||
|
{
|
||||||
|
return Results.Redirect("/assets/amaterasu2sei_2.png", permanent: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
var entityTag = settings.HostImageUpdatedAt?.ToUnixTimeSeconds().ToString(System.Globalization.CultureInfo.InvariantCulture)
|
||||||
|
?? imageData.Length.ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||||
|
return Results.File(
|
||||||
|
imageData,
|
||||||
|
settings.HostImageContentType,
|
||||||
|
entityTag: new Microsoft.Net.Http.Headers.EntityTagHeaderValue($"\"host-{entityTag}\""),
|
||||||
|
lastModified: settings.HostImageUpdatedAt);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,14 +11,21 @@ public static partial class PublicEndpoints
|
|||||||
{
|
{
|
||||||
private static async Task<IResult> CreateNomination(
|
private static async Task<IResult> CreateNomination(
|
||||||
HttpContext context,
|
HttpContext context,
|
||||||
CreateNominationRequest request,
|
CreateNominationRequest? request,
|
||||||
AwardsDbContext db,
|
AwardsDbContext db,
|
||||||
IUserSessionService userSessionService,
|
IUserSessionService userSessionService,
|
||||||
IRiskFlagService riskFlagService,
|
IRiskFlagService riskFlagService,
|
||||||
IRiskRuleService riskRuleService,
|
IRiskRuleService riskRuleService,
|
||||||
NominationEnrichmentService nominationEnrichmentService)
|
NominationEnrichmentService nominationEnrichmentService)
|
||||||
{
|
{
|
||||||
var submittedNominations = NormalizeSubmittedNominations(request);
|
var validationError = ValidateCreateNominationRequest(request);
|
||||||
|
if (validationError is not null)
|
||||||
|
{
|
||||||
|
return validationError;
|
||||||
|
}
|
||||||
|
|
||||||
|
var validatedRequest = request!;
|
||||||
|
var submittedNominations = NormalizeSubmittedNominations(validatedRequest);
|
||||||
|
|
||||||
if (submittedNominations.Length == 0)
|
if (submittedNominations.Length == 0)
|
||||||
{
|
{
|
||||||
@@ -66,14 +73,14 @@ public static partial class PublicEndpoints
|
|||||||
return Results.BadRequest(new { message = "Dieser Link kann nicht nominiert werden. Bitte reiche einen direkten Kanal- oder Profil-Link ein." });
|
return Results.BadRequest(new { message = "Dieser Link kann nicht nominiert werden. Bitte reiche einen direkten Kanal- oder Profil-Link ein." });
|
||||||
}
|
}
|
||||||
|
|
||||||
var season = await db.Seasons.FirstOrDefaultAsync(item => item.Year == request.Year);
|
var season = await db.Seasons.FirstOrDefaultAsync(item => item.Year == validatedRequest.Year);
|
||||||
|
|
||||||
if (season is null)
|
if (season is null)
|
||||||
{
|
{
|
||||||
return Results.BadRequest(new { message = "The selected season does not exist." });
|
return Results.BadRequest(new { message = "The selected season does not exist." });
|
||||||
}
|
}
|
||||||
|
|
||||||
var categoryGroupName = await ResolveCategoryGroupNameAsync(db, season.Id, request, context.RequestAborted);
|
var categoryGroupName = await ResolveCategoryGroupNameAsync(db, season.Id, validatedRequest, context.RequestAborted);
|
||||||
if (string.IsNullOrWhiteSpace(categoryGroupName))
|
if (string.IsNullOrWhiteSpace(categoryGroupName))
|
||||||
{
|
{
|
||||||
return Results.BadRequest(new { message = "The selected category group does not exist for this season." });
|
return Results.BadRequest(new { message = "The selected category group does not exist for this season." });
|
||||||
@@ -102,7 +109,7 @@ public static partial class PublicEndpoints
|
|||||||
return nominationSeasonResolution.Result;
|
return nominationSeasonResolution.Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
var submitterIdResult = await ResolveSubmitterIdAsync(context, request.TwitchUserId, userSessionService);
|
var submitterIdResult = await ResolveSubmitterIdAsync(context, validatedRequest.TwitchUserId, userSessionService);
|
||||||
if (submitterIdResult.Result is not null)
|
if (submitterIdResult.Result is not null)
|
||||||
{
|
{
|
||||||
return submitterIdResult.Result;
|
return submitterIdResult.Result;
|
||||||
|
|||||||
@@ -89,8 +89,23 @@ public static partial class PublicEndpoints
|
|||||||
.OrderByDescending(item => item.Year)
|
.OrderByDescending(item => item.Year)
|
||||||
.ToArrayAsync();
|
.ToArrayAsync();
|
||||||
|
|
||||||
|
var archivedWinnerYearRows = await db.ArchivedWinners
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(item => latestPublishedWinnerYear == null || item.Year < latestPublishedWinnerYear.Value)
|
||||||
|
.GroupBy(item => item.Year)
|
||||||
|
.Select(group => new
|
||||||
|
{
|
||||||
|
Year = group.Key,
|
||||||
|
WinnerCount = group.Count(),
|
||||||
|
})
|
||||||
|
.ToArrayAsync();
|
||||||
|
|
||||||
var archiveYears = archiveYearRows
|
var archiveYears = archiveYearRows
|
||||||
.Select(item => new ArchiveYearDto(item.Year, item.WinnerCount))
|
.Select(item => new ArchiveYearDto(item.Year, item.WinnerCount))
|
||||||
|
.Concat(archivedWinnerYearRows.Select(item => new ArchiveYearDto(item.Year, item.WinnerCount)))
|
||||||
|
.GroupBy(item => item.Year)
|
||||||
|
.Select(group => group.Last())
|
||||||
|
.OrderByDescending(item => item.Year)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
var phaseKey = SeasonMappings.NormalizePhaseKey(season.CurrentPhase);
|
var phaseKey = SeasonMappings.NormalizePhaseKey(season.CurrentPhase);
|
||||||
@@ -150,6 +165,8 @@ public static partial class PublicEndpoints
|
|||||||
new PublicSiteContentDto(
|
new PublicSiteContentDto(
|
||||||
siteSettings.HostDisplayName,
|
siteSettings.HostDisplayName,
|
||||||
siteSettings.HostTagline,
|
siteSettings.HostTagline,
|
||||||
|
siteSettings.HostArtistName,
|
||||||
|
AdminSiteSettingsEndpoints.BuildHostImageUrl(siteSettings),
|
||||||
siteSettings.NewsletterUrl,
|
siteSettings.NewsletterUrl,
|
||||||
siteSettings.ShareXUrl,
|
siteSettings.ShareXUrl,
|
||||||
siteSettings.ShareDiscordUrl,
|
siteSettings.ShareDiscordUrl,
|
||||||
|
|||||||
@@ -12,37 +12,39 @@ public static partial class PublicEndpoints
|
|||||||
{
|
{
|
||||||
private static async Task<IResult> CreateVote(
|
private static async Task<IResult> CreateVote(
|
||||||
HttpContext context,
|
HttpContext context,
|
||||||
CreateVoteRequest request,
|
CreateVoteRequest? request,
|
||||||
AwardsDbContext db,
|
AwardsDbContext db,
|
||||||
IUserSessionService userSessionService,
|
IUserSessionService userSessionService,
|
||||||
IRiskFlagService riskFlagService,
|
IRiskFlagService riskFlagService,
|
||||||
IRiskRuleService riskRuleService)
|
IRiskRuleService riskRuleService)
|
||||||
{
|
{
|
||||||
if (request.Entries.Length == 0)
|
var validationError = ValidateCreateVoteRequest(request);
|
||||||
|
if (validationError is not null)
|
||||||
{
|
{
|
||||||
return Results.BadRequest(new { message = "At least one vote entry is required." });
|
return validationError;
|
||||||
}
|
}
|
||||||
|
|
||||||
var distinctCategoryCount = request.Entries
|
var validatedRequest = request!;
|
||||||
|
var distinctCategoryCount = validatedRequest.Entries
|
||||||
.Select(item => item.CategoryId)
|
.Select(item => item.CategoryId)
|
||||||
.Distinct()
|
.Distinct()
|
||||||
.Count();
|
.Count();
|
||||||
|
|
||||||
if (distinctCategoryCount != request.Entries.Length)
|
if (distinctCategoryCount != validatedRequest.Entries.Length)
|
||||||
{
|
{
|
||||||
return Results.BadRequest(new { message = "Only one vote entry per category is allowed." });
|
return Results.BadRequest(new { message = "Only one vote entry per category is allowed." });
|
||||||
}
|
}
|
||||||
|
|
||||||
var season = await db.Seasons
|
var season = await db.Seasons
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.FirstOrDefaultAsync(item => item.Id == request.SeasonId);
|
.FirstOrDefaultAsync(item => item.Id == validatedRequest.SeasonId);
|
||||||
var voteSeasonResolution = EnsurePublicWriteSeason(season, "voting");
|
var voteSeasonResolution = EnsurePublicWriteSeason(season, "voting");
|
||||||
if (voteSeasonResolution.Result is not null)
|
if (voteSeasonResolution.Result is not null)
|
||||||
{
|
{
|
||||||
return voteSeasonResolution.Result;
|
return voteSeasonResolution.Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
var submitterIdResult = await ResolveSubmitterIdAsync(context, request.TwitchUserId, userSessionService);
|
var submitterIdResult = await ResolveSubmitterIdAsync(context, validatedRequest.TwitchUserId, userSessionService);
|
||||||
if (submitterIdResult.Result is not null)
|
if (submitterIdResult.Result is not null)
|
||||||
{
|
{
|
||||||
return submitterIdResult.Result;
|
return submitterIdResult.Result;
|
||||||
@@ -50,10 +52,10 @@ public static partial class PublicEndpoints
|
|||||||
|
|
||||||
var submitterId = submitterIdResult.SubmitterId!;
|
var submitterId = submitterIdResult.SubmitterId!;
|
||||||
var requestMetadata = RequestMetadataReader.Read(context);
|
var requestMetadata = RequestMetadataReader.Read(context);
|
||||||
var candidateIds = request.Entries.Select(item => item.CandidateId).Distinct().ToArray();
|
var candidateIds = validatedRequest.Entries.Select(item => item.CandidateId).Distinct().ToArray();
|
||||||
var validCandidates = await db.Candidates
|
var validCandidates = await db.Candidates
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.Where(item => item.SeasonId == request.SeasonId && candidateIds.Contains(item.Id))
|
.Where(item => item.SeasonId == validatedRequest.SeasonId && candidateIds.Contains(item.Id))
|
||||||
.Select(item => new { item.Id, item.CategoryId })
|
.Select(item => new { item.Id, item.CategoryId })
|
||||||
.ToArrayAsync();
|
.ToArrayAsync();
|
||||||
|
|
||||||
@@ -63,17 +65,17 @@ public static partial class PublicEndpoints
|
|||||||
}
|
}
|
||||||
|
|
||||||
var candidateCategoryMap = validCandidates.ToDictionary(item => item.Id, item => item.CategoryId);
|
var candidateCategoryMap = validCandidates.ToDictionary(item => item.Id, item => item.CategoryId);
|
||||||
if (request.Entries.Any(item => candidateCategoryMap[item.CandidateId] != item.CategoryId))
|
if (validatedRequest.Entries.Any(item => candidateCategoryMap[item.CandidateId] != item.CategoryId))
|
||||||
{
|
{
|
||||||
return Results.BadRequest(new { message = "A selected candidate does not match the submitted category." });
|
return Results.BadRequest(new { message = "A selected candidate does not match the submitted category." });
|
||||||
}
|
}
|
||||||
|
|
||||||
var ballot = await db.VoteBallots
|
var ballot = await db.VoteBallots
|
||||||
.Include(item => item.Entries)
|
.Include(item => item.Entries)
|
||||||
.FirstOrDefaultAsync(item => item.SeasonId == request.SeasonId && item.SubmittedByTwitchId == submitterId);
|
.FirstOrDefaultAsync(item => item.SeasonId == validatedRequest.SeasonId && item.SubmittedByTwitchId == submitterId);
|
||||||
|
|
||||||
var isResubmission = ballot is not null;
|
var isResubmission = ballot is not null;
|
||||||
ballot = await ApplyVoteEntriesAsync(db, ballot, request.SeasonId, submitterId, request.Entries, context.RequestAborted);
|
ballot = await ApplyVoteEntriesAsync(db, ballot, validatedRequest.SeasonId, submitterId, validatedRequest.Entries, context.RequestAborted);
|
||||||
|
|
||||||
var resubmittedBallotRule = await riskRuleService.GetRuleAsync("resubmitted_ballot", context.RequestAborted);
|
var resubmittedBallotRule = await riskRuleService.GetRuleAsync("resubmitted_ballot", context.RequestAborted);
|
||||||
var rapidVoteUpdatesRule = await riskRuleService.GetRuleAsync("rapid_vote_updates", context.RequestAborted);
|
var rapidVoteUpdatesRule = await riskRuleService.GetRuleAsync("rapid_vote_updates", context.RequestAborted);
|
||||||
@@ -90,9 +92,9 @@ public static partial class PublicEndpoints
|
|||||||
db.ChangeTracker.Clear();
|
db.ChangeTracker.Clear();
|
||||||
ballot = await db.VoteBallots
|
ballot = await db.VoteBallots
|
||||||
.Include(item => item.Entries)
|
.Include(item => item.Entries)
|
||||||
.FirstAsync(item => item.SeasonId == request.SeasonId && item.SubmittedByTwitchId == submitterId, context.RequestAborted);
|
.FirstAsync(item => item.SeasonId == validatedRequest.SeasonId && item.SubmittedByTwitchId == submitterId, context.RequestAborted);
|
||||||
isResubmission = true;
|
isResubmission = true;
|
||||||
ballot = await ApplyVoteEntriesAsync(db, ballot, request.SeasonId, submitterId, request.Entries, context.RequestAborted);
|
ballot = await ApplyVoteEntriesAsync(db, ballot, validatedRequest.SeasonId, submitterId, validatedRequest.Entries, context.RequestAborted);
|
||||||
await db.SaveChangesAsync(context.RequestAborted);
|
await db.SaveChangesAsync(context.RequestAborted);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,21 +109,21 @@ public static partial class PublicEndpoints
|
|||||||
if (isResubmission && resubmittedBallotRule.Enabled)
|
if (isResubmission && resubmittedBallotRule.Enabled)
|
||||||
{
|
{
|
||||||
await riskFlagService.AddIfMissingAsync(
|
await riskFlagService.AddIfMissingAsync(
|
||||||
request.SeasonId,
|
validatedRequest.SeasonId,
|
||||||
submitterId,
|
submitterId,
|
||||||
"vote",
|
"vote",
|
||||||
"resubmitted_ballot",
|
"resubmitted_ballot",
|
||||||
resubmittedBallotRule.Severity,
|
resubmittedBallotRule.Severity,
|
||||||
"Ein User hat sein Ballot erneut gespeichert oder aktualisiert.",
|
"Ein User hat sein Ballot erneut gespeichert oder aktualisiert.",
|
||||||
requestMetadata,
|
requestMetadata,
|
||||||
new { ballotId = ballot.Id, entryCount = request.Entries.Length, entityLinks = new[] { ballotLink } },
|
new { ballotId = ballot.Id, entryCount = validatedRequest.Entries.Length, entityLinks = new[] { ballotLink } },
|
||||||
context.RequestAborted);
|
context.RequestAborted);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rapidVoteUpdatesRule.Enabled && recentVoteSubmissions >= rapidVoteUpdatesRule.Threshold)
|
if (rapidVoteUpdatesRule.Enabled && recentVoteSubmissions >= rapidVoteUpdatesRule.Threshold)
|
||||||
{
|
{
|
||||||
await riskFlagService.AddIfMissingAsync(
|
await riskFlagService.AddIfMissingAsync(
|
||||||
request.SeasonId,
|
validatedRequest.SeasonId,
|
||||||
submitterId,
|
submitterId,
|
||||||
"vote",
|
"vote",
|
||||||
"rapid_vote_updates",
|
"rapid_vote_updates",
|
||||||
|
|||||||
@@ -9,6 +9,42 @@ public static partial class PublicEndpoints
|
|||||||
{
|
{
|
||||||
private static async Task<IResult> GetWinnerArchive(int year, AwardsDbContext db)
|
private static async Task<IResult> GetWinnerArchive(int year, AwardsDbContext db)
|
||||||
{
|
{
|
||||||
|
var latestPublishedWinnerYear = await db.Results
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(result => result.Season.WinnersPublishedAt != null)
|
||||||
|
.Select(result => (int?)result.Season.Year)
|
||||||
|
.MaxAsync();
|
||||||
|
|
||||||
|
var archivedWinnerRows = await db.ArchivedWinners
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(item => item.Year == year)
|
||||||
|
.OrderBy(item => item.Category)
|
||||||
|
.ThenBy(item => item.Subcategory)
|
||||||
|
.ThenBy(item => item.WinnerName)
|
||||||
|
.ToArrayAsync();
|
||||||
|
if (archivedWinnerRows.Length > 0)
|
||||||
|
{
|
||||||
|
var archivedItems = archivedWinnerRows
|
||||||
|
.Select(item =>
|
||||||
|
{
|
||||||
|
var metadata = SeasonMappings.InferProfileMetadataFromUrl(item.WinnerUrl, item.WinnerName);
|
||||||
|
return new WinnerArchiveItemDto(
|
||||||
|
item.Subcategory,
|
||||||
|
item.Category,
|
||||||
|
item.WinnerName,
|
||||||
|
metadata.Slug,
|
||||||
|
metadata.Platform,
|
||||||
|
item.WinnerUrl,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null);
|
||||||
|
})
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
return Results.Ok(new WinnerArchiveResponse(year, archivedItems));
|
||||||
|
}
|
||||||
|
|
||||||
var season = await db.Seasons
|
var season = await db.Seasons
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.Where(item => item.Year == year)
|
.Where(item => item.Year == year)
|
||||||
@@ -16,7 +52,7 @@ public static partial class PublicEndpoints
|
|||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
if (season is null)
|
if (season is null)
|
||||||
{
|
{
|
||||||
return Results.NotFound();
|
return Results.Ok(new WinnerArchiveResponse(year, []));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (season.WinnersPublishedAt is null)
|
if (season.WinnersPublishedAt is null)
|
||||||
@@ -24,11 +60,6 @@ public static partial class PublicEndpoints
|
|||||||
return Results.Ok(new WinnerArchiveResponse(year, []));
|
return Results.Ok(new WinnerArchiveResponse(year, []));
|
||||||
}
|
}
|
||||||
|
|
||||||
var latestPublishedWinnerYear = await db.Results
|
|
||||||
.AsNoTracking()
|
|
||||||
.Where(result => result.Season.WinnersPublishedAt != null)
|
|
||||||
.Select(result => (int?)result.Season.Year)
|
|
||||||
.MaxAsync();
|
|
||||||
if (latestPublishedWinnerYear == season.Year)
|
if (latestPublishedWinnerYear == season.Year)
|
||||||
{
|
{
|
||||||
return Results.Ok(new WinnerArchiveResponse(year, []));
|
return Results.Ok(new WinnerArchiveResponse(year, []));
|
||||||
|
|||||||
@@ -0,0 +1,833 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
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("20260617060000_InitialCreate")]
|
||||||
|
partial class InitialCreate
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
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.AwardResult", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<int>("CandidateId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("CategoryName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(120)
|
||||||
|
.HasColumnType("character varying(120)");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CandidateId");
|
||||||
|
|
||||||
|
b.HasIndex("SeasonId");
|
||||||
|
|
||||||
|
b.ToTable("Results");
|
||||||
|
|
||||||
|
b.HasData(
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 1,
|
||||||
|
CandidateId = 8,
|
||||||
|
CategoryName = "VTuber des Jahres",
|
||||||
|
SeasonId = 2
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 2,
|
||||||
|
CandidateId = 9,
|
||||||
|
CategoryName = "Bestes Live Event",
|
||||||
|
SeasonId = 2
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 3,
|
||||||
|
CandidateId = 10,
|
||||||
|
CategoryName = "Clip des Jahres",
|
||||||
|
SeasonId = 2
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 4,
|
||||||
|
CandidateId = 11,
|
||||||
|
CategoryName = "VTuber des Jahres",
|
||||||
|
SeasonId = 3
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 5,
|
||||||
|
CandidateId = 12,
|
||||||
|
CategoryName = "Clip des Jahres",
|
||||||
|
SeasonId = 3
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 6,
|
||||||
|
CandidateId = 13,
|
||||||
|
CategoryName = "VTuber des Jahres",
|
||||||
|
SeasonId = 4
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Backend.Domain.Candidate", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<int>("CategoryId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("ChannelSlug")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(120)
|
||||||
|
.HasColumnType("character varying(120)");
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(120)
|
||||||
|
.HasColumnType("character varying(120)");
|
||||||
|
|
||||||
|
b.Property<string>("Platform")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(40)
|
||||||
|
.HasColumnType("character varying(40)");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CategoryId");
|
||||||
|
|
||||||
|
b.HasIndex("SeasonId");
|
||||||
|
|
||||||
|
b.ToTable("Candidates");
|
||||||
|
|
||||||
|
b.HasData(
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 1,
|
||||||
|
CategoryId = 1,
|
||||||
|
ChannelSlug = "@hoshimimiyu",
|
||||||
|
DisplayName = "Hoshimi Miyu",
|
||||||
|
Platform = "Twitch",
|
||||||
|
SeasonId = 1
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 2,
|
||||||
|
CategoryId = 1,
|
||||||
|
ChannelSlug = "@kurainu",
|
||||||
|
DisplayName = "Kurainu",
|
||||||
|
Platform = "Twitch",
|
||||||
|
SeasonId = 1
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 3,
|
||||||
|
CategoryId = 1,
|
||||||
|
ChannelSlug = "@shiroch",
|
||||||
|
DisplayName = "Shiro Ch.",
|
||||||
|
Platform = "Twitch",
|
||||||
|
SeasonId = 1
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 4,
|
||||||
|
CategoryId = 2,
|
||||||
|
ChannelSlug = "@kurainu",
|
||||||
|
DisplayName = "Kurainu 3D Live",
|
||||||
|
Platform = "Twitch",
|
||||||
|
SeasonId = 1
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 5,
|
||||||
|
CategoryId = 2,
|
||||||
|
ChannelSlug = "@aoisakura",
|
||||||
|
DisplayName = "Aoi Sakura Showcase",
|
||||||
|
Platform = "YouTube",
|
||||||
|
SeasonId = 1
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 6,
|
||||||
|
CategoryId = 3,
|
||||||
|
ChannelSlug = "@pyonkichikingdom",
|
||||||
|
DisplayName = "Pyonkichi Kingdom",
|
||||||
|
Platform = "Twitch",
|
||||||
|
SeasonId = 1
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 7,
|
||||||
|
CategoryId = 4,
|
||||||
|
ChannelSlug = "@moonrelay",
|
||||||
|
DisplayName = "Moonrelay",
|
||||||
|
Platform = "Twitch",
|
||||||
|
SeasonId = 1
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 8,
|
||||||
|
CategoryId = 5,
|
||||||
|
ChannelSlug = "@hoshimimiyu",
|
||||||
|
DisplayName = "Hoshimi Miyu",
|
||||||
|
Platform = "Twitch",
|
||||||
|
SeasonId = 2
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 9,
|
||||||
|
CategoryId = 6,
|
||||||
|
ChannelSlug = "@kurainu",
|
||||||
|
DisplayName = "Kurainu 3D Live",
|
||||||
|
Platform = "Twitch",
|
||||||
|
SeasonId = 2
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 10,
|
||||||
|
CategoryId = 7,
|
||||||
|
ChannelSlug = "@pyonkichikingdom",
|
||||||
|
DisplayName = "Pyonkichi Kingdom",
|
||||||
|
Platform = "Twitch",
|
||||||
|
SeasonId = 2
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 11,
|
||||||
|
CategoryId = 8,
|
||||||
|
ChannelSlug = "@aoisakura",
|
||||||
|
DisplayName = "Aoi Sakura",
|
||||||
|
Platform = "YouTube",
|
||||||
|
SeasonId = 3
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 12,
|
||||||
|
CategoryId = 9,
|
||||||
|
ChannelSlug = "@starbyte",
|
||||||
|
DisplayName = "Starbyte",
|
||||||
|
Platform = "Twitch",
|
||||||
|
SeasonId = 3
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 13,
|
||||||
|
CategoryId = 10,
|
||||||
|
ChannelSlug = "@tenshivox",
|
||||||
|
DisplayName = "Tenshi Vox",
|
||||||
|
Platform = "Twitch",
|
||||||
|
SeasonId = 4
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Backend.Domain.Category", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(400)
|
||||||
|
.HasColumnType("character varying(400)");
|
||||||
|
|
||||||
|
b.Property<string>("GroupName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(80)
|
||||||
|
.HasColumnType("character varying(80)");
|
||||||
|
|
||||||
|
b.Property<int>("MaxNomineesPerUser")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(120)
|
||||||
|
.HasColumnType("character varying(120)");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("Slug")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<int>("SortOrder")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("SeasonId", "Slug")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("Categories");
|
||||||
|
|
||||||
|
b.HasData(
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 1,
|
||||||
|
Description = "Die groesste 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.Nomination", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<int?>("CandidateId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("CandidateText")
|
||||||
|
.HasMaxLength(120)
|
||||||
|
.HasColumnType("character varying(120)");
|
||||||
|
|
||||||
|
b.Property<int>("CategoryId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("SubmittedByTwitchId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(120)
|
||||||
|
.HasColumnType("character varying(120)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CandidateId");
|
||||||
|
|
||||||
|
b.HasIndex("CategoryId");
|
||||||
|
|
||||||
|
b.HasIndex("SeasonId");
|
||||||
|
|
||||||
|
b.ToTable("Nominations");
|
||||||
|
|
||||||
|
b.HasData(
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 1,
|
||||||
|
CandidateText = "Hoshimi Miyu",
|
||||||
|
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,
|
||||||
|
SubmittedByTwitchId = "twitch_hoshi"
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 2,
|
||||||
|
CandidateText = "Kurainu 3D Live",
|
||||||
|
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,
|
||||||
|
SubmittedByTwitchId = "twitch_kurainu"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Backend.Domain.Season", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("CurrentPhase")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(60)
|
||||||
|
.HasColumnType("character varying(60)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsCommunityOnly")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<bool>("IsCurrent")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(160)
|
||||||
|
.HasColumnType("character varying(160)");
|
||||||
|
|
||||||
|
b.Property<DateOnly>("NominationEndsAt")
|
||||||
|
.HasColumnType("date");
|
||||||
|
|
||||||
|
b.Property<DateOnly>("NominationStartsAt")
|
||||||
|
.HasColumnType("date");
|
||||||
|
|
||||||
|
b.Property<DateOnly>("ReviewEndsAt")
|
||||||
|
.HasColumnType("date");
|
||||||
|
|
||||||
|
b.Property<DateOnly>("ReviewStartsAt")
|
||||||
|
.HasColumnType("date");
|
||||||
|
|
||||||
|
b.Property<DateOnly>("ShowDate")
|
||||||
|
.HasColumnType("date");
|
||||||
|
|
||||||
|
b.Property<DateOnly>("VotingEndsAt")
|
||||||
|
.HasColumnType("date");
|
||||||
|
|
||||||
|
b.Property<DateOnly>("VotingStartsAt")
|
||||||
|
.HasColumnType("date");
|
||||||
|
|
||||||
|
b.Property<int>("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),
|
||||||
|
VotingEndsAt = new DateOnly(2026, 6, 30),
|
||||||
|
VotingStartsAt = new DateOnly(2026, 6, 1),
|
||||||
|
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),
|
||||||
|
VotingEndsAt = new DateOnly(2025, 6, 30),
|
||||||
|
VotingStartsAt = new DateOnly(2025, 6, 1),
|
||||||
|
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),
|
||||||
|
VotingEndsAt = new DateOnly(2024, 6, 30),
|
||||||
|
VotingStartsAt = new DateOnly(2024, 6, 1),
|
||||||
|
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),
|
||||||
|
VotingEndsAt = new DateOnly(2023, 6, 30),
|
||||||
|
VotingStartsAt = new DateOnly(2023, 6, 1),
|
||||||
|
Year = 2023
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Backend.Domain.VoteBallot", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<int>("SeasonId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("Status")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(30)
|
||||||
|
.HasColumnType("character varying(30)");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("SubmittedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("SubmittedByTwitchId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(120)
|
||||||
|
.HasColumnType("character varying(120)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("SeasonId");
|
||||||
|
|
||||||
|
b.ToTable("VoteBallots");
|
||||||
|
|
||||||
|
b.HasData(
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 1,
|
||||||
|
SeasonId = 1,
|
||||||
|
Status = "submitted",
|
||||||
|
SubmittedAt = new DateTimeOffset(new DateTime(2026, 6, 11, 12, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
|
||||||
|
SubmittedByTwitchId = "twitch_vote_1"
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 2,
|
||||||
|
SeasonId = 1,
|
||||||
|
Status = "submitted",
|
||||||
|
SubmittedAt = new DateTimeOffset(new DateTime(2026, 6, 11, 12, 5, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
|
||||||
|
SubmittedByTwitchId = "twitch_vote_2"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Backend.Domain.VoteEntry", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<int>("BallotId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<int>("CandidateId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<int>("CategoryId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("BallotId");
|
||||||
|
|
||||||
|
b.HasIndex("CandidateId");
|
||||||
|
|
||||||
|
b.HasIndex("CategoryId");
|
||||||
|
|
||||||
|
b.ToTable("VoteEntries");
|
||||||
|
|
||||||
|
b.HasData(
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 1,
|
||||||
|
BallotId = 1,
|
||||||
|
CandidateId = 1,
|
||||||
|
CategoryId = 1
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 2,
|
||||||
|
BallotId = 1,
|
||||||
|
CandidateId = 4,
|
||||||
|
CategoryId = 2
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 3,
|
||||||
|
BallotId = 2,
|
||||||
|
CandidateId = 2,
|
||||||
|
CategoryId = 1
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 4,
|
||||||
|
BallotId = 2,
|
||||||
|
CandidateId = 6,
|
||||||
|
CategoryId = 3
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Backend.Domain.AwardResult", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Backend.Domain.Candidate", "Candidate")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("CandidateId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Backend.Domain.Season", "Season")
|
||||||
|
.WithMany("Results")
|
||||||
|
.HasForeignKey("SeasonId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Candidate");
|
||||||
|
|
||||||
|
b.Navigation("Season");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Backend.Domain.Candidate", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Backend.Domain.Category", "Category")
|
||||||
|
.WithMany("Candidates")
|
||||||
|
.HasForeignKey("CategoryId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Backend.Domain.Season", "Season")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("SeasonId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Category");
|
||||||
|
|
||||||
|
b.Navigation("Season");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Backend.Domain.Category", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Backend.Domain.Season", "Season")
|
||||||
|
.WithMany("Categories")
|
||||||
|
.HasForeignKey("SeasonId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Season");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Backend.Domain.Nomination", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Backend.Domain.Candidate", "Candidate")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("CandidateId");
|
||||||
|
|
||||||
|
b.HasOne("Backend.Domain.Category", "Category")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("CategoryId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Backend.Domain.Season", "Season")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("SeasonId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Candidate");
|
||||||
|
|
||||||
|
b.Navigation("Category");
|
||||||
|
|
||||||
|
b.Navigation("Season");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Backend.Domain.VoteBallot", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Backend.Domain.Season", "Season")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("SeasonId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Season");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Backend.Domain.VoteEntry", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Backend.Domain.VoteBallot", "Ballot")
|
||||||
|
.WithMany("Entries")
|
||||||
|
.HasForeignKey("BallotId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Backend.Domain.Candidate", "Candidate")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("CandidateId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Backend.Domain.Category", "Category")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("CategoryId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Ballot");
|
||||||
|
|
||||||
|
b.Navigation("Candidate");
|
||||||
|
|
||||||
|
b.Navigation("Category");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Backend.Domain.Category", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Candidates");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Backend.Domain.Season", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Categories");
|
||||||
|
|
||||||
|
b.Navigation("Results");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Backend.Domain.VoteBallot", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Entries");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,395 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional
|
||||||
|
|
||||||
|
namespace Backend.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class InitialCreate : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Seasons",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
Year = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
Name = table.Column<string>(type: "character varying(160)", maxLength: 160, nullable: false),
|
||||||
|
IsCurrent = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
IsCommunityOnly = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
CurrentPhase = table.Column<string>(type: "character varying(60)", maxLength: 60, nullable: false),
|
||||||
|
NominationStartsAt = table.Column<DateOnly>(type: "date", nullable: false),
|
||||||
|
NominationEndsAt = table.Column<DateOnly>(type: "date", nullable: false),
|
||||||
|
VotingStartsAt = table.Column<DateOnly>(type: "date", nullable: false),
|
||||||
|
VotingEndsAt = table.Column<DateOnly>(type: "date", nullable: false),
|
||||||
|
ReviewStartsAt = table.Column<DateOnly>(type: "date", nullable: false),
|
||||||
|
ReviewEndsAt = table.Column<DateOnly>(type: "date", nullable: false),
|
||||||
|
ShowDate = table.Column<DateOnly>(type: "date", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Seasons", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Categories",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
SeasonId = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
GroupName = table.Column<string>(type: "character varying(80)", maxLength: 80, nullable: false),
|
||||||
|
Name = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
||||||
|
Slug = table.Column<string>(type: "text", nullable: false),
|
||||||
|
Description = table.Column<string>(type: "character varying(400)", maxLength: 400, nullable: false),
|
||||||
|
SortOrder = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
MaxNomineesPerUser = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Categories", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Categories_Seasons_SeasonId",
|
||||||
|
column: x => x.SeasonId,
|
||||||
|
principalTable: "Seasons",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "VoteBallots",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
SeasonId = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
SubmittedByTwitchId = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
||||||
|
Status = table.Column<string>(type: "character varying(30)", maxLength: 30, nullable: false),
|
||||||
|
SubmittedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_VoteBallots", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_VoteBallots_Seasons_SeasonId",
|
||||||
|
column: x => x.SeasonId,
|
||||||
|
principalTable: "Seasons",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Candidates",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
SeasonId = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
CategoryId = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
DisplayName = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
||||||
|
ChannelSlug = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
||||||
|
Platform = table.Column<string>(type: "character varying(40)", maxLength: 40, nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Candidates", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Candidates_Categories_CategoryId",
|
||||||
|
column: x => x.CategoryId,
|
||||||
|
principalTable: "Categories",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Candidates_Seasons_SeasonId",
|
||||||
|
column: x => x.SeasonId,
|
||||||
|
principalTable: "Seasons",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Nominations",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
SeasonId = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
CategoryId = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
SubmittedByTwitchId = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
||||||
|
CandidateId = table.Column<int>(type: "integer", nullable: true),
|
||||||
|
CandidateText = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: true),
|
||||||
|
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Nominations", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Nominations_Candidates_CandidateId",
|
||||||
|
column: x => x.CandidateId,
|
||||||
|
principalTable: "Candidates",
|
||||||
|
principalColumn: "Id");
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Nominations_Categories_CategoryId",
|
||||||
|
column: x => x.CategoryId,
|
||||||
|
principalTable: "Categories",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Nominations_Seasons_SeasonId",
|
||||||
|
column: x => x.SeasonId,
|
||||||
|
principalTable: "Seasons",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Results",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
SeasonId = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
CandidateId = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
CategoryName = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Results", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Results_Candidates_CandidateId",
|
||||||
|
column: x => x.CandidateId,
|
||||||
|
principalTable: "Candidates",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Results_Seasons_SeasonId",
|
||||||
|
column: x => x.SeasonId,
|
||||||
|
principalTable: "Seasons",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "VoteEntries",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
BallotId = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
CategoryId = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
CandidateId = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_VoteEntries", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_VoteEntries_Candidates_CandidateId",
|
||||||
|
column: x => x.CandidateId,
|
||||||
|
principalTable: "Candidates",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_VoteEntries_Categories_CategoryId",
|
||||||
|
column: x => x.CategoryId,
|
||||||
|
principalTable: "Categories",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_VoteEntries_VoteBallots_BallotId",
|
||||||
|
column: x => x.BallotId,
|
||||||
|
principalTable: "VoteBallots",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.InsertData(
|
||||||
|
table: "Seasons",
|
||||||
|
columns: new[] { "Id", "CurrentPhase", "IsCommunityOnly", "IsCurrent", "Name", "NominationEndsAt", "NominationStartsAt", "ReviewEndsAt", "ReviewStartsAt", "ShowDate", "VotingEndsAt", "VotingStartsAt", "Year" },
|
||||||
|
values: new object[,]
|
||||||
|
{
|
||||||
|
{ 1, "Community Voting", true, true, "VTuber Star Awards 2026", new DateOnly(2026, 5, 31), new DateOnly(2026, 5, 1), new DateOnly(2026, 7, 10), new DateOnly(2026, 7, 1), new DateOnly(2026, 7, 20), new DateOnly(2026, 6, 30), new DateOnly(2026, 6, 1), 2026 },
|
||||||
|
{ 2, "Archived", true, false, "VTuber Star Awards 2025", new DateOnly(2025, 5, 31), new DateOnly(2025, 5, 1), new DateOnly(2025, 7, 10), new DateOnly(2025, 7, 1), new DateOnly(2025, 7, 20), new DateOnly(2025, 6, 30), new DateOnly(2025, 6, 1), 2025 },
|
||||||
|
{ 3, "Archived", true, false, "VTuber Star Awards 2024", new DateOnly(2024, 5, 31), new DateOnly(2024, 5, 1), new DateOnly(2024, 7, 10), new DateOnly(2024, 7, 1), new DateOnly(2024, 7, 20), new DateOnly(2024, 6, 30), new DateOnly(2024, 6, 1), 2024 },
|
||||||
|
{ 4, "Archived", true, false, "VTuber Star Awards 2023", new DateOnly(2023, 5, 31), new DateOnly(2023, 5, 1), new DateOnly(2023, 7, 10), new DateOnly(2023, 7, 1), new DateOnly(2023, 7, 20), new DateOnly(2023, 6, 30), new DateOnly(2023, 6, 1), 2023 }
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.InsertData(
|
||||||
|
table: "Categories",
|
||||||
|
columns: new[] { "Id", "Description", "GroupName", "MaxNomineesPerUser", "Name", "SeasonId", "Slug", "SortOrder" },
|
||||||
|
values: new object[,]
|
||||||
|
{
|
||||||
|
{ 1, "Die groesste Auszeichnung des Jahres.", "Main Awards", 3, "VTuber des Jahres", 1, "vtuber-des-jahres", 1 },
|
||||||
|
{ 2, "Events, Konzerte und 3D-Shows.", "Performance", 3, "Bestes Live Event", 1, "bestes-live-event", 2 },
|
||||||
|
{ 3, "Der lustigste oder emotionalste Clip des Jahres.", "Clips & Highlights", 3, "Clip des Jahres", 1, "clip-des-jahres", 3 },
|
||||||
|
{ 4, "Die aktivste und freundlichste Community.", "Main Awards", 3, "Beste Community", 1, "beste-community", 4 },
|
||||||
|
{ 5, "Archivkategorie 2025.", "Main Awards", 3, "VTuber des Jahres", 2, "vtuber-des-jahres", 1 },
|
||||||
|
{ 6, "Archivkategorie 2025.", "Performance", 3, "Bestes Live Event", 2, "bestes-live-event", 2 },
|
||||||
|
{ 7, "Archivkategorie 2025.", "Clips & Highlights", 3, "Clip des Jahres", 2, "clip-des-jahres", 3 },
|
||||||
|
{ 8, "Archivkategorie 2024.", "Main Awards", 3, "VTuber des Jahres", 3, "vtuber-des-jahres", 1 },
|
||||||
|
{ 9, "Archivkategorie 2024.", "Clips & Highlights", 3, "Clip des Jahres", 3, "clip-des-jahres", 2 },
|
||||||
|
{ 10, "Archivkategorie 2023.", "Main Awards", 3, "VTuber des Jahres", 4, "vtuber-des-jahres", 1 }
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.InsertData(
|
||||||
|
table: "VoteBallots",
|
||||||
|
columns: new[] { "Id", "SeasonId", "Status", "SubmittedAt", "SubmittedByTwitchId" },
|
||||||
|
values: new object[,]
|
||||||
|
{
|
||||||
|
{ 1, 1, "submitted", new DateTimeOffset(new DateTime(2026, 6, 11, 12, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)), "twitch_vote_1" },
|
||||||
|
{ 2, 1, "submitted", new DateTimeOffset(new DateTime(2026, 6, 11, 12, 5, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)), "twitch_vote_2" }
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.InsertData(
|
||||||
|
table: "Candidates",
|
||||||
|
columns: new[] { "Id", "CategoryId", "ChannelSlug", "DisplayName", "Platform", "SeasonId" },
|
||||||
|
values: new object[,]
|
||||||
|
{
|
||||||
|
{ 1, 1, "@hoshimimiyu", "Hoshimi Miyu", "Twitch", 1 },
|
||||||
|
{ 2, 1, "@kurainu", "Kurainu", "Twitch", 1 },
|
||||||
|
{ 3, 1, "@shiroch", "Shiro Ch.", "Twitch", 1 },
|
||||||
|
{ 4, 2, "@kurainu", "Kurainu 3D Live", "Twitch", 1 },
|
||||||
|
{ 5, 2, "@aoisakura", "Aoi Sakura Showcase", "YouTube", 1 },
|
||||||
|
{ 6, 3, "@pyonkichikingdom", "Pyonkichi Kingdom", "Twitch", 1 },
|
||||||
|
{ 7, 4, "@moonrelay", "Moonrelay", "Twitch", 1 },
|
||||||
|
{ 8, 5, "@hoshimimiyu", "Hoshimi Miyu", "Twitch", 2 },
|
||||||
|
{ 9, 6, "@kurainu", "Kurainu 3D Live", "Twitch", 2 },
|
||||||
|
{ 10, 7, "@pyonkichikingdom", "Pyonkichi Kingdom", "Twitch", 2 },
|
||||||
|
{ 11, 8, "@aoisakura", "Aoi Sakura", "YouTube", 3 },
|
||||||
|
{ 12, 9, "@starbyte", "Starbyte", "Twitch", 3 },
|
||||||
|
{ 13, 10, "@tenshivox", "Tenshi Vox", "Twitch", 4 }
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.InsertData(
|
||||||
|
table: "Nominations",
|
||||||
|
columns: new[] { "Id", "CandidateId", "CandidateText", "CategoryId", "CreatedAt", "SeasonId", "SubmittedByTwitchId" },
|
||||||
|
values: new object[,]
|
||||||
|
{
|
||||||
|
{ 1, null, "Hoshimi Miyu", 1, new DateTimeOffset(new DateTime(2026, 6, 10, 13, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)), 1, "twitch_hoshi" },
|
||||||
|
{ 2, null, "Kurainu 3D Live", 2, new DateTimeOffset(new DateTime(2026, 6, 10, 14, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)), 1, "twitch_kurainu" }
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.InsertData(
|
||||||
|
table: "Results",
|
||||||
|
columns: new[] { "Id", "CandidateId", "CategoryName", "SeasonId" },
|
||||||
|
values: new object[,]
|
||||||
|
{
|
||||||
|
{ 1, 8, "VTuber des Jahres", 2 },
|
||||||
|
{ 2, 9, "Bestes Live Event", 2 },
|
||||||
|
{ 3, 10, "Clip des Jahres", 2 },
|
||||||
|
{ 4, 11, "VTuber des Jahres", 3 },
|
||||||
|
{ 5, 12, "Clip des Jahres", 3 },
|
||||||
|
{ 6, 13, "VTuber des Jahres", 4 }
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.InsertData(
|
||||||
|
table: "VoteEntries",
|
||||||
|
columns: new[] { "Id", "BallotId", "CandidateId", "CategoryId" },
|
||||||
|
values: new object[,]
|
||||||
|
{
|
||||||
|
{ 1, 1, 1, 1 },
|
||||||
|
{ 2, 1, 4, 2 },
|
||||||
|
{ 3, 2, 2, 1 },
|
||||||
|
{ 4, 2, 6, 3 }
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Candidates_CategoryId",
|
||||||
|
table: "Candidates",
|
||||||
|
column: "CategoryId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Candidates_SeasonId",
|
||||||
|
table: "Candidates",
|
||||||
|
column: "SeasonId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Categories_SeasonId_Slug",
|
||||||
|
table: "Categories",
|
||||||
|
columns: new[] { "SeasonId", "Slug" },
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Nominations_CandidateId",
|
||||||
|
table: "Nominations",
|
||||||
|
column: "CandidateId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Nominations_CategoryId",
|
||||||
|
table: "Nominations",
|
||||||
|
column: "CategoryId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Nominations_SeasonId",
|
||||||
|
table: "Nominations",
|
||||||
|
column: "SeasonId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Results_CandidateId",
|
||||||
|
table: "Results",
|
||||||
|
column: "CandidateId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Results_SeasonId",
|
||||||
|
table: "Results",
|
||||||
|
column: "SeasonId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Seasons_Year",
|
||||||
|
table: "Seasons",
|
||||||
|
column: "Year",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_VoteBallots_SeasonId",
|
||||||
|
table: "VoteBallots",
|
||||||
|
column: "SeasonId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_VoteEntries_BallotId",
|
||||||
|
table: "VoteEntries",
|
||||||
|
column: "BallotId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_VoteEntries_CandidateId",
|
||||||
|
table: "VoteEntries",
|
||||||
|
column: "CandidateId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_VoteEntries_CategoryId",
|
||||||
|
table: "VoteEntries",
|
||||||
|
column: "CategoryId");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Nominations");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Results");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "VoteEntries");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Candidates");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "VoteBallots");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Categories");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Seasons");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,42 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddClipReviewWorkflow : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql(
|
||||||
|
"""
|
||||||
|
ALTER TABLE IF EXISTS "ClipSubmissions"
|
||||||
|
ADD COLUMN IF NOT EXISTS "ReviewNote" character varying(500) NULL;
|
||||||
|
|
||||||
|
ALTER TABLE IF EXISTS "ClipSubmissions"
|
||||||
|
ADD COLUMN IF NOT EXISTS "ReviewedByTwitchId" character varying(120) NULL;
|
||||||
|
|
||||||
|
ALTER TABLE IF EXISTS "ClipSubmissions"
|
||||||
|
ADD COLUMN IF NOT EXISTS "ReviewedAt" timestamp with time zone NULL;
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql(
|
||||||
|
"""
|
||||||
|
ALTER TABLE IF EXISTS "ClipSubmissions"
|
||||||
|
DROP COLUMN IF EXISTS "ReviewNote";
|
||||||
|
|
||||||
|
ALTER TABLE IF EXISTS "ClipSubmissions"
|
||||||
|
DROP COLUMN IF EXISTS "ReviewedByTwitchId";
|
||||||
|
|
||||||
|
ALTER TABLE IF EXISTS "ClipSubmissions"
|
||||||
|
DROP COLUMN IF EXISTS "ReviewedAt";
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,95 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddNominationReviewWorkflow : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_Nominations_SeasonId",
|
||||||
|
table: "Nominations");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "ReviewNote",
|
||||||
|
table: "Nominations",
|
||||||
|
type: "character varying(500)",
|
||||||
|
maxLength: 500,
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<DateTimeOffset>(
|
||||||
|
name: "ReviewedAt",
|
||||||
|
table: "Nominations",
|
||||||
|
type: "timestamp with time zone",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "ReviewedByTwitchId",
|
||||||
|
table: "Nominations",
|
||||||
|
type: "character varying(120)",
|
||||||
|
maxLength: 120,
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "Status",
|
||||||
|
table: "Nominations",
|
||||||
|
type: "character varying(20)",
|
||||||
|
maxLength: 20,
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Nominations",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 1,
|
||||||
|
columns: new[] { "ReviewNote", "ReviewedAt", "ReviewedByTwitchId", "Status" },
|
||||||
|
values: new object[] { null, null, null, "pending" });
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Nominations",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 2,
|
||||||
|
columns: new[] { "ReviewNote", "ReviewedAt", "ReviewedByTwitchId", "Status" },
|
||||||
|
values: new object[] { null, null, null, "pending" });
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Nominations_SeasonId_Status",
|
||||||
|
table: "Nominations",
|
||||||
|
columns: new[] { "SeasonId", "Status" });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_Nominations_SeasonId_Status",
|
||||||
|
table: "Nominations");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "ReviewNote",
|
||||||
|
table: "Nominations");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "ReviewedAt",
|
||||||
|
table: "Nominations");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "ReviewedByTwitchId",
|
||||||
|
table: "Nominations");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "Status",
|
||||||
|
table: "Nominations");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Nominations_SeasonId",
|
||||||
|
table: "Nominations",
|
||||||
|
column: "SeasonId");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,93 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddAwardResultCategoryLock : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_Results_SeasonId",
|
||||||
|
table: "Results");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "CategoryId",
|
||||||
|
table: "Results",
|
||||||
|
type: "integer",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.Sql(
|
||||||
|
"""
|
||||||
|
UPDATE "Results" AS r
|
||||||
|
SET "CategoryId" = c."Id"
|
||||||
|
FROM "Categories" AS c
|
||||||
|
WHERE r."SeasonId" = c."SeasonId"
|
||||||
|
AND lower(trim(r."CategoryName")) = lower(trim(c."Name"));
|
||||||
|
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF EXISTS (SELECT 1 FROM "Results" WHERE "CategoryId" IS NULL) THEN
|
||||||
|
RAISE EXCEPTION 'Could not backfill CategoryId for one or more rows in Results.';
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
""");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<int>(
|
||||||
|
name: "CategoryId",
|
||||||
|
table: "Results",
|
||||||
|
type: "integer",
|
||||||
|
nullable: false,
|
||||||
|
oldClrType: typeof(int),
|
||||||
|
oldType: "integer",
|
||||||
|
oldNullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Results_CategoryId",
|
||||||
|
table: "Results",
|
||||||
|
column: "CategoryId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Results_SeasonId_CategoryId",
|
||||||
|
table: "Results",
|
||||||
|
columns: new[] { "SeasonId", "CategoryId" },
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_Results_Categories_CategoryId",
|
||||||
|
table: "Results",
|
||||||
|
column: "CategoryId",
|
||||||
|
principalTable: "Categories",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_Results_Categories_CategoryId",
|
||||||
|
table: "Results");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_Results_CategoryId",
|
||||||
|
table: "Results");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_Results_SeasonId_CategoryId",
|
||||||
|
table: "Results");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "CategoryId",
|
||||||
|
table: "Results");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Results_SeasonId",
|
||||||
|
table: "Results",
|
||||||
|
column: "SeasonId");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,58 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddSeasonShowStreamUrl : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "ShowStreamUrl",
|
||||||
|
table: "Seasons",
|
||||||
|
type: "character varying(400)",
|
||||||
|
maxLength: 400,
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Seasons",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 1,
|
||||||
|
column: "ShowStreamUrl",
|
||||||
|
value: "https://twitch.tv/jayuhime");
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Seasons",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 2,
|
||||||
|
column: "ShowStreamUrl",
|
||||||
|
value: "https://twitch.tv/jayuhime");
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Seasons",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 3,
|
||||||
|
column: "ShowStreamUrl",
|
||||||
|
value: "https://youtube.com/c/Jayuhime");
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Seasons",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 4,
|
||||||
|
column: "ShowStreamUrl",
|
||||||
|
value: "https://twitch.tv/jayuhime");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "ShowStreamUrl",
|
||||||
|
table: "Seasons");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,48 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddSiteSettings : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "SiteSettings",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
HostDisplayName = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
||||||
|
HostTagline = table.Column<string>(type: "character varying(160)", maxLength: 160, nullable: false),
|
||||||
|
NewsletterUrl = table.Column<string>(type: "character varying(400)", maxLength: 400, nullable: false),
|
||||||
|
PrivacyEmail = table.Column<string>(type: "character varying(160)", maxLength: 160, nullable: false),
|
||||||
|
ImprintUrl = table.Column<string>(type: "character varying(400)", maxLength: 400, nullable: false),
|
||||||
|
ContactUrl = table.Column<string>(type: "character varying(400)", maxLength: 400, nullable: false),
|
||||||
|
SponsorsUrl = table.Column<string>(type: "character varying(400)", maxLength: 400, nullable: false),
|
||||||
|
SocialLinksJson = table.Column<string>(type: "text", nullable: false),
|
||||||
|
FaqJson = table.Column<string>(type: "text", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_SiteSettings", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.InsertData(
|
||||||
|
table: "SiteSettings",
|
||||||
|
columns: new[] { "Id", "ContactUrl", "FaqJson", "HostDisplayName", "HostTagline", "ImprintUrl", "NewsletterUrl", "PrivacyEmail", "SocialLinksJson", "SponsorsUrl" },
|
||||||
|
values: new object[] { 1, "https://vtuber-star-awards.de/kontakt", "[{\"question\":\"Wer darf mitmachen?\",\"answer\":\"Jede:r mit einem Twitch-Account. Einmal einloggen gen\\u00FCgt \\u2013 kein extra Konto, kein Papierkram.\"},{\"question\":\"Wie werden die Gewinner bestimmt?\",\"answer\":\"Komplett durch eure Stimmen. Die Community entscheidet, wer auf die B\\u00FChne darf.\"},{\"question\":\"Kann ich meine Wahl noch \\u00E4ndern?\",\"answer\":\"Ja. Bis zum Ende der Voting-Phase kannst du Nominierungen und Stimmen anpassen.\"},{\"question\":\"Wann und wo findet die Show statt?\",\"answer\":\"Die gro\\u00DFe Live-Show wird von Jayuhime gehostet und live auf den verkn\\u00FCpften Plattformen \\u00FCbertragen.\"}]", "Jayuhime", "VTuber & Award Host", "https://vtuber-star-awards.de/impressum", "https://vtuber-star-awards.de/newsletter", "datenschutz@vtuber-star-awards.de", "[{\"label\":\"Twitch\",\"platform\":\"twitch\",\"url\":\"https://twitch.tv/jayuhime\"},{\"label\":\"YouTube\",\"platform\":\"youtube\",\"url\":\"https://youtube.com/c/Jayuhime\"},{\"label\":\"X\",\"platform\":\"x\",\"url\":\"https://x.com/jayuhime\"},{\"label\":\"Instagram\",\"platform\":\"instagram\",\"url\":\"https://instagram.com/jayuhime\"},{\"label\":\"Discord\",\"platform\":\"discord\",\"url\":\"https://discord.gg/jayuhime\"}]", "https://vtuber-star-awards.de/partner" });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "SiteSettings");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1213
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,58 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddPrivacyPolicyContentMetadata : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "PrivacyPolicyContent",
|
||||||
|
table: "SiteSettings",
|
||||||
|
type: "text",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<DateTimeOffset>(
|
||||||
|
name: "PrivacyPolicyUpdatedAt",
|
||||||
|
table: "SiteSettings",
|
||||||
|
type: "timestamp with time zone",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "PrivacyPolicyUpdatedBy",
|
||||||
|
table: "SiteSettings",
|
||||||
|
type: "character varying(120)",
|
||||||
|
maxLength: 120,
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "SiteSettings",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 1,
|
||||||
|
columns: new[] { "PrivacyPolicyContent", "PrivacyPolicyUpdatedAt", "PrivacyPolicyUpdatedBy" },
|
||||||
|
values: new object[] { "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.", new DateTimeOffset(new DateTime(2026, 6, 23, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)), "seed" });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "PrivacyPolicyContent",
|
||||||
|
table: "SiteSettings");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "PrivacyPolicyUpdatedAt",
|
||||||
|
table: "SiteSettings");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "PrivacyPolicyUpdatedBy",
|
||||||
|
table: "SiteSettings");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,143 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddOperationalSiteSettings : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "DemoLoginDisplayName",
|
||||||
|
table: "SiteSettings",
|
||||||
|
type: "character varying(120)",
|
||||||
|
maxLength: 120,
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "DemoLoginEmail",
|
||||||
|
table: "SiteSettings",
|
||||||
|
type: "character varying(180)",
|
||||||
|
maxLength: 180,
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<bool>(
|
||||||
|
name: "DemoLoginEnabled",
|
||||||
|
table: "SiteSettings",
|
||||||
|
type: "boolean",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: false);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<bool>(
|
||||||
|
name: "DemoLoginManagedByDatabase",
|
||||||
|
table: "SiteSettings",
|
||||||
|
type: "boolean",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: false);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "DemoLoginPasswordHash",
|
||||||
|
table: "SiteSettings",
|
||||||
|
type: "character varying(120)",
|
||||||
|
maxLength: 120,
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "DemoLoginPasswordSalt",
|
||||||
|
table: "SiteSettings",
|
||||||
|
type: "character varying(80)",
|
||||||
|
maxLength: 80,
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "DemoLoginTwitchUserId",
|
||||||
|
table: "SiteSettings",
|
||||||
|
type: "character varying(120)",
|
||||||
|
maxLength: 120,
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "MaintenanceMessage",
|
||||||
|
table: "SiteSettings",
|
||||||
|
type: "character varying(600)",
|
||||||
|
maxLength: 600,
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<bool>(
|
||||||
|
name: "MaintenanceModeEnabled",
|
||||||
|
table: "SiteSettings",
|
||||||
|
type: "boolean",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: false);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "MaintenanceTitle",
|
||||||
|
table: "SiteSettings",
|
||||||
|
type: "character varying(120)",
|
||||||
|
maxLength: 120,
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "SiteSettings",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 1,
|
||||||
|
columns: new[] { "DemoLoginDisplayName", "DemoLoginEmail", "DemoLoginEnabled", "DemoLoginManagedByDatabase", "DemoLoginPasswordHash", "DemoLoginPasswordSalt", "DemoLoginTwitchUserId", "MaintenanceMessage", "MaintenanceModeEnabled", "MaintenanceTitle" },
|
||||||
|
values: new object[] { "Jayuhime Admin", "", false, false, "", "", "jayuhime_admin", "Die Award-Galaxie wird gerade liebevoll poliert. Schau gleich wieder vorbei.", false, "Sternenpause" });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "DemoLoginDisplayName",
|
||||||
|
table: "SiteSettings");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "DemoLoginEmail",
|
||||||
|
table: "SiteSettings");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "DemoLoginEnabled",
|
||||||
|
table: "SiteSettings");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "DemoLoginManagedByDatabase",
|
||||||
|
table: "SiteSettings");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "DemoLoginPasswordHash",
|
||||||
|
table: "SiteSettings");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "DemoLoginPasswordSalt",
|
||||||
|
table: "SiteSettings");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "DemoLoginTwitchUserId",
|
||||||
|
table: "SiteSettings");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "MaintenanceMessage",
|
||||||
|
table: "SiteSettings");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "MaintenanceModeEnabled",
|
||||||
|
table: "SiteSettings");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "MaintenanceTitle",
|
||||||
|
table: "SiteSettings");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,57 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddSeasonShowStartsAt : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql(
|
||||||
|
"""
|
||||||
|
ALTER TABLE "Seasons"
|
||||||
|
ADD COLUMN IF NOT EXISTS "ShowStartsAt" time without time zone NOT NULL DEFAULT TIME '20:00:00';
|
||||||
|
""");
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Seasons",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 1,
|
||||||
|
column: "ShowStartsAt",
|
||||||
|
value: new TimeOnly(20, 0, 0));
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Seasons",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 2,
|
||||||
|
column: "ShowStartsAt",
|
||||||
|
value: new TimeOnly(20, 0, 0));
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Seasons",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 3,
|
||||||
|
column: "ShowStartsAt",
|
||||||
|
value: new TimeOnly(20, 0, 0));
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Seasons",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 4,
|
||||||
|
column: "ShowStartsAt",
|
||||||
|
value: new TimeOnly(20, 0, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "ShowStartsAt",
|
||||||
|
table: "Seasons");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,54 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddClipCandidateLink : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql(
|
||||||
|
"""
|
||||||
|
ALTER TABLE IF EXISTS "ClipSubmissions"
|
||||||
|
ADD COLUMN IF NOT EXISTS "CandidateId" integer NULL;
|
||||||
|
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF to_regclass('"ClipSubmissions"') IS NOT NULL THEN
|
||||||
|
CREATE INDEX IF NOT EXISTS "IX_ClipSubmissions_CandidateId"
|
||||||
|
ON "ClipSubmissions" ("CandidateId");
|
||||||
|
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_constraint
|
||||||
|
WHERE conname = 'FK_ClipSubmissions_Candidates_CandidateId'
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE "ClipSubmissions"
|
||||||
|
ADD CONSTRAINT "FK_ClipSubmissions_Candidates_CandidateId"
|
||||||
|
FOREIGN KEY ("CandidateId") REFERENCES "Candidates" ("Id")
|
||||||
|
ON DELETE SET NULL;
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql(
|
||||||
|
"""
|
||||||
|
ALTER TABLE IF EXISTS "ClipSubmissions"
|
||||||
|
DROP CONSTRAINT IF EXISTS "FK_ClipSubmissions_Candidates_CandidateId";
|
||||||
|
|
||||||
|
DROP INDEX IF EXISTS "IX_ClipSubmissions_CandidateId";
|
||||||
|
|
||||||
|
ALTER TABLE IF EXISTS "ClipSubmissions"
|
||||||
|
DROP COLUMN IF EXISTS "CandidateId";
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
using Backend.Data;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
[DbContext(typeof(AwardsDbContext))]
|
||||||
|
[Migration("20260624150500_AddRiskFlagReviewNote")]
|
||||||
|
public partial class AddRiskFlagReviewNote : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql(
|
||||||
|
"""
|
||||||
|
ALTER TABLE IF EXISTS "RiskFlags"
|
||||||
|
ADD COLUMN IF NOT EXISTS "ReviewNote" character varying(500) NULL;
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql(
|
||||||
|
"""
|
||||||
|
ALTER TABLE IF EXISTS "RiskFlags"
|
||||||
|
DROP COLUMN IF EXISTS "ReviewNote";
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
using Backend.Data;
|
||||||
|
using Backend.Services;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
[DbContext(typeof(AwardsDbContext))]
|
||||||
|
[Migration("20260624162000_AddRiskRulesJson")]
|
||||||
|
public partial class AddRiskRulesJson : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql(
|
||||||
|
$"""
|
||||||
|
ALTER TABLE IF EXISTS "SiteSettings"
|
||||||
|
ADD COLUMN IF NOT EXISTS "RiskRulesJson" text NOT NULL DEFAULT '{RiskRuleSettings.Serialize(RiskRuleSettings.Defaults).Replace("'", "''")}';
|
||||||
|
|
||||||
|
UPDATE "SiteSettings"
|
||||||
|
SET "RiskRulesJson" = '{RiskRuleSettings.Serialize(RiskRuleSettings.Defaults).Replace("'", "''")}'
|
||||||
|
WHERE "RiskRulesJson" IS NULL OR btrim("RiskRulesJson") = '' OR "RiskRulesJson" = '[]';
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql(
|
||||||
|
"""
|
||||||
|
ALTER TABLE IF EXISTS "SiteSettings"
|
||||||
|
DROP COLUMN IF EXISTS "RiskRulesJson";
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
using Backend.Data;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
[DbContext(typeof(AwardsDbContext))]
|
||||||
|
[Migration("20260625110000_AddFooterPageContent")]
|
||||||
|
public partial class AddFooterPageContent : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
var imprintContent = SeedCatalog.DefaultImprintContent.Replace("'", "''");
|
||||||
|
var contactContent = SeedCatalog.DefaultContactContent.Replace("'", "''");
|
||||||
|
var sponsorsContent = SeedCatalog.DefaultSponsorsContent.Replace("'", "''");
|
||||||
|
|
||||||
|
migrationBuilder.Sql(
|
||||||
|
$"""
|
||||||
|
ALTER TABLE IF EXISTS "SiteSettings"
|
||||||
|
ADD COLUMN IF NOT EXISTS "ImprintContent" text NOT NULL DEFAULT '',
|
||||||
|
ADD COLUMN IF NOT EXISTS "ContactContent" text NOT NULL DEFAULT '',
|
||||||
|
ADD COLUMN IF NOT EXISTS "SponsorsContent" text NOT NULL DEFAULT '';
|
||||||
|
|
||||||
|
UPDATE "SiteSettings"
|
||||||
|
SET "ImprintContent" = '{imprintContent}'
|
||||||
|
WHERE "ImprintContent" IS NULL OR btrim("ImprintContent") = '';
|
||||||
|
|
||||||
|
UPDATE "SiteSettings"
|
||||||
|
SET "ContactContent" = '{contactContent}'
|
||||||
|
WHERE "ContactContent" IS NULL OR btrim("ContactContent") = '';
|
||||||
|
|
||||||
|
UPDATE "SiteSettings"
|
||||||
|
SET "SponsorsContent" = '{sponsorsContent}'
|
||||||
|
WHERE "SponsorsContent" IS NULL OR btrim("SponsorsContent") = '';
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql(
|
||||||
|
"""
|
||||||
|
ALTER TABLE IF EXISTS "SiteSettings"
|
||||||
|
DROP COLUMN IF EXISTS "ImprintContent",
|
||||||
|
DROP COLUMN IF EXISTS "ContactContent",
|
||||||
|
DROP COLUMN IF EXISTS "SponsorsContent";
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,79 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddTeamManagement : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "TeamMembers",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
Login = table.Column<string>(type: "character varying(80)", maxLength: 80, nullable: false),
|
||||||
|
DisplayName = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
||||||
|
Role = table.Column<string>(type: "character varying(40)", maxLength: 40, nullable: false),
|
||||||
|
PasswordHash = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
||||||
|
PasswordSalt = table.Column<string>(type: "character varying(80)", maxLength: 80, nullable: false),
|
||||||
|
MustChangePassword = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
IsActive = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
CreatedByTwitchId = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
||||||
|
UpdatedByTwitchId = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: true),
|
||||||
|
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||||
|
UpdatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||||
|
LastLoginAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||||
|
PasswordResetAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_TeamMembers", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "TeamRolePermissions",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
Role = table.Column<string>(type: "character varying(40)", maxLength: 40, nullable: false),
|
||||||
|
PermissionsJson = table.Column<string>(type: "text", nullable: false),
|
||||||
|
UpdatedByTwitchId = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
||||||
|
UpdatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_TeamRolePermissions", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_TeamMembers_Login",
|
||||||
|
table: "TeamMembers",
|
||||||
|
column: "Login",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_TeamRolePermissions_Role",
|
||||||
|
table: "TeamRolePermissions",
|
||||||
|
column: "Role",
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "TeamMembers");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "TeamRolePermissions");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
using System;
|
||||||
|
using Backend.Data;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
[DbContext(typeof(AwardsDbContext))]
|
||||||
|
[Migration("20260625153000_AddCreatorRoleAndTeamTwitchBinding")]
|
||||||
|
public partial class AddCreatorRoleAndTeamTwitchBinding : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "BoundTwitchDisplayName",
|
||||||
|
table: "TeamMembers",
|
||||||
|
type: "character varying(120)",
|
||||||
|
maxLength: 120,
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "BoundTwitchUserId",
|
||||||
|
table: "TeamMembers",
|
||||||
|
type: "character varying(120)",
|
||||||
|
maxLength: 120,
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<DateTimeOffset>(
|
||||||
|
name: "TwitchBoundAt",
|
||||||
|
table: "TeamMembers",
|
||||||
|
type: "timestamp with time zone",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_TeamMembers_BoundTwitchUserId",
|
||||||
|
table: "TeamMembers",
|
||||||
|
column: "BoundTwitchUserId",
|
||||||
|
unique: true,
|
||||||
|
filter: "\"BoundTwitchUserId\" IS NOT NULL");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_TeamMembers_BoundTwitchUserId",
|
||||||
|
table: "TeamMembers");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "BoundTwitchDisplayName",
|
||||||
|
table: "TeamMembers");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "BoundTwitchUserId",
|
||||||
|
table: "TeamMembers");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "TwitchBoundAt",
|
||||||
|
table: "TeamMembers");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1471
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1527
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,177 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddCandidatePreparationFields : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "AcceptanceNote",
|
||||||
|
table: "Candidates",
|
||||||
|
type: "character varying(500)",
|
||||||
|
maxLength: 500,
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "AcceptanceStatus",
|
||||||
|
table: "Candidates",
|
||||||
|
type: "character varying(30)",
|
||||||
|
maxLength: 30,
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "open");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "ClipCompilationPlatform",
|
||||||
|
table: "Candidates",
|
||||||
|
type: "character varying(40)",
|
||||||
|
maxLength: 40,
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "ClipCompilationTitle",
|
||||||
|
table: "Candidates",
|
||||||
|
type: "character varying(200)",
|
||||||
|
maxLength: 200,
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "ClipCompilationUrl",
|
||||||
|
table: "Candidates",
|
||||||
|
type: "character varying(500)",
|
||||||
|
maxLength: 500,
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "ClipEmbedStatus",
|
||||||
|
table: "Candidates",
|
||||||
|
type: "character varying(30)",
|
||||||
|
maxLength: 30,
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "unchecked");
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Candidates",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 1,
|
||||||
|
columns: new[] { "AcceptanceNote", "AcceptanceStatus", "ClipCompilationPlatform", "ClipCompilationTitle", "ClipCompilationUrl", "ClipEmbedStatus" },
|
||||||
|
values: new object[] { null, "open", null, null, null, "unchecked" });
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Candidates",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 2,
|
||||||
|
columns: new[] { "AcceptanceNote", "AcceptanceStatus", "ClipCompilationPlatform", "ClipCompilationTitle", "ClipCompilationUrl", "ClipEmbedStatus" },
|
||||||
|
values: new object[] { null, "open", null, null, null, "unchecked" });
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Candidates",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 3,
|
||||||
|
columns: new[] { "AcceptanceNote", "AcceptanceStatus", "ClipCompilationPlatform", "ClipCompilationTitle", "ClipCompilationUrl", "ClipEmbedStatus" },
|
||||||
|
values: new object[] { null, "open", null, null, null, "unchecked" });
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Candidates",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 4,
|
||||||
|
columns: new[] { "AcceptanceNote", "AcceptanceStatus", "ClipCompilationPlatform", "ClipCompilationTitle", "ClipCompilationUrl", "ClipEmbedStatus" },
|
||||||
|
values: new object[] { null, "open", null, null, null, "unchecked" });
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Candidates",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 5,
|
||||||
|
columns: new[] { "AcceptanceNote", "AcceptanceStatus", "ClipCompilationPlatform", "ClipCompilationTitle", "ClipCompilationUrl", "ClipEmbedStatus" },
|
||||||
|
values: new object[] { null, "open", null, null, null, "unchecked" });
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Candidates",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 6,
|
||||||
|
columns: new[] { "AcceptanceNote", "AcceptanceStatus", "ClipCompilationPlatform", "ClipCompilationTitle", "ClipCompilationUrl", "ClipEmbedStatus" },
|
||||||
|
values: new object[] { null, "open", null, null, null, "unchecked" });
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Candidates",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 7,
|
||||||
|
columns: new[] { "AcceptanceNote", "AcceptanceStatus", "ClipCompilationPlatform", "ClipCompilationTitle", "ClipCompilationUrl", "ClipEmbedStatus" },
|
||||||
|
values: new object[] { null, "open", null, null, null, "unchecked" });
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Candidates",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 8,
|
||||||
|
columns: new[] { "AcceptanceNote", "AcceptanceStatus", "ClipCompilationPlatform", "ClipCompilationTitle", "ClipCompilationUrl", "ClipEmbedStatus" },
|
||||||
|
values: new object[] { null, "open", null, null, null, "unchecked" });
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Candidates",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 9,
|
||||||
|
columns: new[] { "AcceptanceNote", "AcceptanceStatus", "ClipCompilationPlatform", "ClipCompilationTitle", "ClipCompilationUrl", "ClipEmbedStatus" },
|
||||||
|
values: new object[] { null, "open", null, null, null, "unchecked" });
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Candidates",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 10,
|
||||||
|
columns: new[] { "AcceptanceNote", "AcceptanceStatus", "ClipCompilationPlatform", "ClipCompilationTitle", "ClipCompilationUrl", "ClipEmbedStatus" },
|
||||||
|
values: new object[] { null, "open", null, null, null, "unchecked" });
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Candidates",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 11,
|
||||||
|
columns: new[] { "AcceptanceNote", "AcceptanceStatus", "ClipCompilationPlatform", "ClipCompilationTitle", "ClipCompilationUrl", "ClipEmbedStatus" },
|
||||||
|
values: new object[] { null, "open", null, null, null, "unchecked" });
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Candidates",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 12,
|
||||||
|
columns: new[] { "AcceptanceNote", "AcceptanceStatus", "ClipCompilationPlatform", "ClipCompilationTitle", "ClipCompilationUrl", "ClipEmbedStatus" },
|
||||||
|
values: new object[] { null, "open", null, null, null, "unchecked" });
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Candidates",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 13,
|
||||||
|
columns: new[] { "AcceptanceNote", "AcceptanceStatus", "ClipCompilationPlatform", "ClipCompilationTitle", "ClipCompilationUrl", "ClipEmbedStatus" },
|
||||||
|
values: new object[] { null, "open", null, null, null, "unchecked" });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "AcceptanceNote",
|
||||||
|
table: "Candidates");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "AcceptanceStatus",
|
||||||
|
table: "Candidates");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "ClipCompilationPlatform",
|
||||||
|
table: "Candidates");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "ClipCompilationTitle",
|
||||||
|
table: "Candidates");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "ClipCompilationUrl",
|
||||||
|
table: "Candidates");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "ClipEmbedStatus",
|
||||||
|
table: "Candidates");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,35 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddWorkflowRulesJson : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql("""
|
||||||
|
ALTER TABLE "SiteSettings"
|
||||||
|
ADD COLUMN IF NOT EXISTS "WorkflowRulesJson" text NOT NULL DEFAULT '[]';
|
||||||
|
""");
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "SiteSettings",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 1,
|
||||||
|
column: "WorkflowRulesJson",
|
||||||
|
value: "[{\"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.\"}]");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql("""
|
||||||
|
ALTER TABLE "SiteSettings"
|
||||||
|
DROP COLUMN IF EXISTS "WorkflowRulesJson";
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1552
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,55 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddOptionalClipFeatureSettings : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql("""
|
||||||
|
ALTER TABLE "SiteSettings"
|
||||||
|
ADD COLUMN IF NOT EXISTS "ClipReviewEnabled" boolean NOT NULL DEFAULT true;
|
||||||
|
""");
|
||||||
|
|
||||||
|
migrationBuilder.Sql("""
|
||||||
|
ALTER TABLE "SiteSettings"
|
||||||
|
ADD COLUMN IF NOT EXISTS "ClipSubmissionDisabledMessage" character varying(240) NOT NULL DEFAULT 'Clip-Einreichungen sind aktuell geschlossen.';
|
||||||
|
""");
|
||||||
|
|
||||||
|
migrationBuilder.Sql("""
|
||||||
|
ALTER TABLE "SiteSettings"
|
||||||
|
ADD COLUMN IF NOT EXISTS "ClipSubmissionsEnabled" boolean NOT NULL DEFAULT false;
|
||||||
|
""");
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "SiteSettings",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 1,
|
||||||
|
columns: new[] { "ClipReviewEnabled", "ClipSubmissionDisabledMessage" },
|
||||||
|
values: new object[] { true, "Clip-Einreichungen sind aktuell geschlossen." });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql("""
|
||||||
|
ALTER TABLE "SiteSettings"
|
||||||
|
DROP COLUMN IF EXISTS "ClipReviewEnabled";
|
||||||
|
""");
|
||||||
|
|
||||||
|
migrationBuilder.Sql("""
|
||||||
|
ALTER TABLE "SiteSettings"
|
||||||
|
DROP COLUMN IF EXISTS "ClipSubmissionDisabledMessage";
|
||||||
|
""");
|
||||||
|
|
||||||
|
migrationBuilder.Sql("""
|
||||||
|
ALTER TABLE "SiteSettings"
|
||||||
|
DROP COLUMN IF EXISTS "ClipSubmissionsEnabled";
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,36 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddNominationLinkBlacklist : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "NominationLinkBlacklistJson",
|
||||||
|
table: "SiteSettings",
|
||||||
|
type: "text",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "[]");
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "SiteSettings",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 1,
|
||||||
|
column: "NominationLinkBlacklistJson",
|
||||||
|
value: "[{\"Url\":\"https://kick.com/\"},{\"Url\":\"https://www.twitch.tv/\"},{\"Url\":\"https://www.youtube.com/\"}]");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "NominationLinkBlacklistJson",
|
||||||
|
table: "SiteSettings");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,152 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddClipAdminMenuVisible : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql("""
|
||||||
|
ALTER TABLE "SiteSettings"
|
||||||
|
ADD COLUMN IF NOT EXISTS "ClipAdminMenuVisible" boolean NOT NULL DEFAULT FALSE;
|
||||||
|
|
||||||
|
ALTER TABLE "SiteSettings"
|
||||||
|
ADD COLUMN IF NOT EXISTS "ShowactApplicationDisabledMessage" character varying(240) NOT NULL DEFAULT '';
|
||||||
|
|
||||||
|
ALTER TABLE "SiteSettings"
|
||||||
|
ADD COLUMN IF NOT EXISTS "ShowactApplicationsEnabled" boolean NOT NULL DEFAULT FALSE;
|
||||||
|
|
||||||
|
ALTER TABLE "SiteSettings"
|
||||||
|
ADD COLUMN IF NOT EXISTS "ShowactsContent" text NOT NULL DEFAULT '';
|
||||||
|
|
||||||
|
ALTER TABLE "SiteSettings"
|
||||||
|
ADD COLUMN IF NOT EXISTS "ShowactsUrl" character varying(400) NOT NULL DEFAULT '';
|
||||||
|
|
||||||
|
ALTER TABLE "SiteSettings"
|
||||||
|
ADD COLUMN IF NOT EXISTS "SponsorsVisible" boolean NOT NULL DEFAULT TRUE;
|
||||||
|
""");
|
||||||
|
|
||||||
|
migrationBuilder.Sql("""
|
||||||
|
CREATE TABLE IF NOT EXISTS "ShowactApplications" (
|
||||||
|
"Id" integer GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||||
|
"SeasonId" integer NOT NULL,
|
||||||
|
"ArtistName" character varying(120) NOT NULL,
|
||||||
|
"ContactEmail" character varying(180) NOT NULL,
|
||||||
|
"ContactDiscord" character varying(120) NOT NULL,
|
||||||
|
"PlatformUrl" character varying(500) NOT NULL,
|
||||||
|
"PerformanceType" character varying(80) NOT NULL,
|
||||||
|
"Description" character varying(1000) NOT NULL,
|
||||||
|
"TechnicalNotes" character varying(1000) NOT NULL,
|
||||||
|
"ReferenceUrl" character varying(500) NOT NULL,
|
||||||
|
"Status" character varying(20) NOT NULL,
|
||||||
|
"ReviewNote" character varying(500) NULL,
|
||||||
|
"ReviewedByTwitchId" character varying(120) NULL,
|
||||||
|
"CreatedFromIp" character varying(80) NOT NULL,
|
||||||
|
"UserAgent" character varying(400) NOT NULL,
|
||||||
|
"CreatedAt" timestamp with time zone NOT NULL,
|
||||||
|
"ReviewedAt" timestamp with time zone NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS "Sponsors" (
|
||||||
|
"Id" integer GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||||
|
"SeasonId" integer NOT NULL,
|
||||||
|
"Name" character varying(120) NOT NULL,
|
||||||
|
"WebsiteUrl" character varying(500) NOT NULL,
|
||||||
|
"LogoUrl" character varying(500) NOT NULL,
|
||||||
|
"Description" character varying(500) NOT NULL,
|
||||||
|
"Tier" character varying(80) NOT NULL,
|
||||||
|
"SortOrder" integer NOT NULL,
|
||||||
|
"IsVisible" boolean NOT NULL,
|
||||||
|
"CreatedAt" timestamp with time zone NOT NULL,
|
||||||
|
"UpdatedAt" timestamp with time zone NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_constraint
|
||||||
|
WHERE conname = 'FK_ShowactApplications_Seasons_SeasonId'
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE "ShowactApplications"
|
||||||
|
ADD CONSTRAINT "FK_ShowactApplications_Seasons_SeasonId"
|
||||||
|
FOREIGN KEY ("SeasonId") REFERENCES "Seasons" ("Id")
|
||||||
|
ON DELETE CASCADE;
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_constraint
|
||||||
|
WHERE conname = 'FK_Sponsors_Seasons_SeasonId'
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE "Sponsors"
|
||||||
|
ADD CONSTRAINT "FK_Sponsors_Seasons_SeasonId"
|
||||||
|
FOREIGN KEY ("SeasonId") REFERENCES "Seasons" ("Id")
|
||||||
|
ON DELETE CASCADE;
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
""");
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "SiteSettings",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 1,
|
||||||
|
columns: new[] { "ClipAdminMenuVisible", "ShowactApplicationDisabledMessage", "ShowactsContent", "ShowactsUrl", "SponsorsVisible", "WorkflowRulesJson" },
|
||||||
|
values: new object[] { true, "Showact-Bewerbungen sind aktuell geschlossen.", "Informationen zu Showact-Bewerbungen, Ablauf und Kontakt fuer die Award-Show.", "https://vtuber-star-awards.de/showacts", true, "[{\"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.\"}]" });
|
||||||
|
|
||||||
|
migrationBuilder.Sql("""
|
||||||
|
CREATE INDEX IF NOT EXISTS "IX_ShowactApplications_SeasonId_Status"
|
||||||
|
ON "ShowactApplications" ("SeasonId", "Status");
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS "IX_Sponsors_SeasonId_IsVisible_SortOrder"
|
||||||
|
ON "Sponsors" ("SeasonId", "IsVisible", "SortOrder");
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql("""
|
||||||
|
DROP TABLE IF EXISTS "ShowactApplications";
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS "Sponsors";
|
||||||
|
""");
|
||||||
|
|
||||||
|
migrationBuilder.Sql("""
|
||||||
|
ALTER TABLE "SiteSettings"
|
||||||
|
DROP COLUMN IF EXISTS "ClipAdminMenuVisible";
|
||||||
|
|
||||||
|
ALTER TABLE "SiteSettings"
|
||||||
|
DROP COLUMN IF EXISTS "ShowactApplicationDisabledMessage";
|
||||||
|
|
||||||
|
ALTER TABLE "SiteSettings"
|
||||||
|
DROP COLUMN IF EXISTS "ShowactApplicationsEnabled";
|
||||||
|
|
||||||
|
ALTER TABLE "SiteSettings"
|
||||||
|
DROP COLUMN IF EXISTS "ShowactsContent";
|
||||||
|
|
||||||
|
ALTER TABLE "SiteSettings"
|
||||||
|
DROP COLUMN IF EXISTS "ShowactsUrl";
|
||||||
|
|
||||||
|
ALTER TABLE "SiteSettings"
|
||||||
|
DROP COLUMN IF EXISTS "SponsorsVisible";
|
||||||
|
""");
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "SiteSettings",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 1,
|
||||||
|
column: "WorkflowRulesJson",
|
||||||
|
value: "[{\"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.\"}]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1768
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,47 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddShareUrls : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "ShareDiscordUrl",
|
||||||
|
table: "SiteSettings",
|
||||||
|
type: "text",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "ShareXUrl",
|
||||||
|
table: "SiteSettings",
|
||||||
|
type: "text",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "SiteSettings",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 1,
|
||||||
|
columns: new[] { "ShareDiscordUrl", "ShareXUrl" },
|
||||||
|
values: new object[] { "", "" });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "ShareDiscordUrl",
|
||||||
|
table: "SiteSettings");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "ShareXUrl",
|
||||||
|
table: "SiteSettings");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,47 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddShowactDynamicForm : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "ShowactFormSchemaJson",
|
||||||
|
table: "SiteSettings",
|
||||||
|
type: "text",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "FieldResponsesJson",
|
||||||
|
table: "ShowactApplications",
|
||||||
|
type: "text",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "SiteSettings",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 1,
|
||||||
|
column: "ShowactFormSchemaJson",
|
||||||
|
value: "[]");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "ShowactFormSchemaJson",
|
||||||
|
table: "SiteSettings");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "FieldResponsesJson",
|
||||||
|
table: "ShowactApplications");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,108 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddCategoryViewerRanges : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "ViewerRangeMax",
|
||||||
|
table: "Categories",
|
||||||
|
type: "integer",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "ViewerRangeMin",
|
||||||
|
table: "Categories",
|
||||||
|
type: "integer",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Categories",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 1,
|
||||||
|
columns: new[] { "ViewerRangeMax", "ViewerRangeMin" },
|
||||||
|
values: new object[] { null, null });
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Categories",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 2,
|
||||||
|
columns: new[] { "ViewerRangeMax", "ViewerRangeMin" },
|
||||||
|
values: new object[] { null, null });
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Categories",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 3,
|
||||||
|
columns: new[] { "ViewerRangeMax", "ViewerRangeMin" },
|
||||||
|
values: new object[] { null, null });
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Categories",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 4,
|
||||||
|
columns: new[] { "ViewerRangeMax", "ViewerRangeMin" },
|
||||||
|
values: new object[] { null, null });
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Categories",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 5,
|
||||||
|
columns: new[] { "ViewerRangeMax", "ViewerRangeMin" },
|
||||||
|
values: new object[] { null, null });
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Categories",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 6,
|
||||||
|
columns: new[] { "ViewerRangeMax", "ViewerRangeMin" },
|
||||||
|
values: new object[] { null, null });
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Categories",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 7,
|
||||||
|
columns: new[] { "ViewerRangeMax", "ViewerRangeMin" },
|
||||||
|
values: new object[] { null, null });
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Categories",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 8,
|
||||||
|
columns: new[] { "ViewerRangeMax", "ViewerRangeMin" },
|
||||||
|
values: new object[] { null, null });
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Categories",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 9,
|
||||||
|
columns: new[] { "ViewerRangeMax", "ViewerRangeMin" },
|
||||||
|
values: new object[] { null, null });
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Categories",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 10,
|
||||||
|
columns: new[] { "ViewerRangeMax", "ViewerRangeMin" },
|
||||||
|
values: new object[] { null, null });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "ViewerRangeMax",
|
||||||
|
table: "Categories");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "ViewerRangeMin",
|
||||||
|
table: "Categories");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1789
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,36 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddSessionIdleTimeoutSettings : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "SessionIdleTimeoutHours",
|
||||||
|
table: "SiteSettings",
|
||||||
|
type: "integer",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 3);
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "SiteSettings",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 1,
|
||||||
|
column: "SessionIdleTimeoutHours",
|
||||||
|
value: 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "SessionIdleTimeoutHours",
|
||||||
|
table: "SiteSettings");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1799
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,57 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddSeasonSubcategoryTemplates : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "SubcategoryTemplatesJson",
|
||||||
|
table: "Seasons",
|
||||||
|
type: "text",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "[]");
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Seasons",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 1,
|
||||||
|
column: "SubcategoryTemplatesJson",
|
||||||
|
value: "[]");
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Seasons",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 2,
|
||||||
|
column: "SubcategoryTemplatesJson",
|
||||||
|
value: "[]");
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Seasons",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 3,
|
||||||
|
column: "SubcategoryTemplatesJson",
|
||||||
|
value: "[]");
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Seasons",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 4,
|
||||||
|
column: "SubcategoryTemplatesJson",
|
||||||
|
value: "[]");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "SubcategoryTemplatesJson",
|
||||||
|
table: "Seasons");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1936
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,388 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddNominationGroupTrackerIdentity : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_Nominations_Categories_CategoryId",
|
||||||
|
table: "Nominations");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<int>(
|
||||||
|
name: "CategoryId",
|
||||||
|
table: "Nominations",
|
||||||
|
type: "integer",
|
||||||
|
nullable: true,
|
||||||
|
oldClrType: typeof(int),
|
||||||
|
oldType: "integer");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "AvgViewers",
|
||||||
|
table: "Nominations",
|
||||||
|
type: "integer",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "CategoryGroupName",
|
||||||
|
table: "Nominations",
|
||||||
|
type: "character varying(80)",
|
||||||
|
maxLength: 80,
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "ResolvedChannel",
|
||||||
|
table: "Nominations",
|
||||||
|
type: "character varying(120)",
|
||||||
|
maxLength: 120,
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "ResolvedPlatform",
|
||||||
|
table: "Nominations",
|
||||||
|
type: "character varying(40)",
|
||||||
|
maxLength: 40,
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "StreamerIdentityId",
|
||||||
|
table: "Nominations",
|
||||||
|
type: "integer",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "SuggestedCategoryId",
|
||||||
|
table: "Nominations",
|
||||||
|
type: "integer",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<DateTimeOffset>(
|
||||||
|
name: "TrackerCheckedAt",
|
||||||
|
table: "Nominations",
|
||||||
|
type: "timestamp with time zone",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "TrackerStatus",
|
||||||
|
table: "Nominations",
|
||||||
|
type: "character varying(40)",
|
||||||
|
maxLength: 40,
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "pending");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "NominationTally",
|
||||||
|
table: "Candidates",
|
||||||
|
type: "integer",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "StreamerIdentityId",
|
||||||
|
table: "Candidates",
|
||||||
|
type: "integer",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "StreamerIdentities",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
Platform = table.Column<string>(type: "character varying(40)", maxLength: 40, nullable: false),
|
||||||
|
Login = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
||||||
|
NormalizedKey = table.Column<string>(type: "character varying(180)", maxLength: 180, nullable: false),
|
||||||
|
DisplayName = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
||||||
|
ProfileUrl = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
|
||||||
|
LastResolvedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_StreamerIdentities", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Candidates",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 1,
|
||||||
|
column: "StreamerIdentityId",
|
||||||
|
value: null);
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Candidates",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 2,
|
||||||
|
column: "StreamerIdentityId",
|
||||||
|
value: null);
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Candidates",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 3,
|
||||||
|
column: "StreamerIdentityId",
|
||||||
|
value: null);
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Candidates",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 4,
|
||||||
|
column: "StreamerIdentityId",
|
||||||
|
value: null);
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Candidates",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 5,
|
||||||
|
column: "StreamerIdentityId",
|
||||||
|
value: null);
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Candidates",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 6,
|
||||||
|
column: "StreamerIdentityId",
|
||||||
|
value: null);
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Candidates",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 7,
|
||||||
|
column: "StreamerIdentityId",
|
||||||
|
value: null);
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Candidates",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 8,
|
||||||
|
column: "StreamerIdentityId",
|
||||||
|
value: null);
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Candidates",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 9,
|
||||||
|
column: "StreamerIdentityId",
|
||||||
|
value: null);
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Candidates",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 10,
|
||||||
|
column: "StreamerIdentityId",
|
||||||
|
value: null);
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Candidates",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 11,
|
||||||
|
column: "StreamerIdentityId",
|
||||||
|
value: null);
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Candidates",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 12,
|
||||||
|
column: "StreamerIdentityId",
|
||||||
|
value: null);
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Candidates",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 13,
|
||||||
|
column: "StreamerIdentityId",
|
||||||
|
value: null);
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Nominations",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 1,
|
||||||
|
columns: new[] { "AvgViewers", "CategoryGroupName", "ResolvedChannel", "ResolvedPlatform", "StreamerIdentityId", "SuggestedCategoryId", "TrackerCheckedAt", "TrackerStatus" },
|
||||||
|
values: new object[] { null, "", null, null, null, null, null, "pending" });
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "Nominations",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 2,
|
||||||
|
columns: new[] { "AvgViewers", "CategoryGroupName", "ResolvedChannel", "ResolvedPlatform", "StreamerIdentityId", "SuggestedCategoryId", "TrackerCheckedAt", "TrackerStatus" },
|
||||||
|
values: new object[] { null, "", null, null, null, null, null, "pending" });
|
||||||
|
|
||||||
|
migrationBuilder.Sql("""
|
||||||
|
UPDATE "Nominations" n
|
||||||
|
SET "CategoryGroupName" = c."GroupName"
|
||||||
|
FROM "Categories" c
|
||||||
|
WHERE n."CategoryId" = c."Id"
|
||||||
|
AND COALESCE(n."CategoryGroupName", '') = '';
|
||||||
|
""");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Nominations_SeasonId_CategoryGroupName_Status",
|
||||||
|
table: "Nominations",
|
||||||
|
columns: new[] { "SeasonId", "CategoryGroupName", "Status" });
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Nominations_SeasonId_StreamerIdentityId_CategoryGroupName",
|
||||||
|
table: "Nominations",
|
||||||
|
columns: new[] { "SeasonId", "StreamerIdentityId", "CategoryGroupName" });
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Nominations_StreamerIdentityId",
|
||||||
|
table: "Nominations",
|
||||||
|
column: "StreamerIdentityId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Nominations_SuggestedCategoryId",
|
||||||
|
table: "Nominations",
|
||||||
|
column: "SuggestedCategoryId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Candidates_StreamerIdentityId",
|
||||||
|
table: "Candidates",
|
||||||
|
column: "StreamerIdentityId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_StreamerIdentities_NormalizedKey",
|
||||||
|
table: "StreamerIdentities",
|
||||||
|
column: "NormalizedKey",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_Candidates_StreamerIdentities_StreamerIdentityId",
|
||||||
|
table: "Candidates",
|
||||||
|
column: "StreamerIdentityId",
|
||||||
|
principalTable: "StreamerIdentities",
|
||||||
|
principalColumn: "Id");
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_Nominations_Categories_CategoryId",
|
||||||
|
table: "Nominations",
|
||||||
|
column: "CategoryId",
|
||||||
|
principalTable: "Categories",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.SetNull);
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_Nominations_Categories_SuggestedCategoryId",
|
||||||
|
table: "Nominations",
|
||||||
|
column: "SuggestedCategoryId",
|
||||||
|
principalTable: "Categories",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.SetNull);
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_Nominations_StreamerIdentities_StreamerIdentityId",
|
||||||
|
table: "Nominations",
|
||||||
|
column: "StreamerIdentityId",
|
||||||
|
principalTable: "StreamerIdentities",
|
||||||
|
principalColumn: "Id");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_Candidates_StreamerIdentities_StreamerIdentityId",
|
||||||
|
table: "Candidates");
|
||||||
|
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_Nominations_Categories_CategoryId",
|
||||||
|
table: "Nominations");
|
||||||
|
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_Nominations_Categories_SuggestedCategoryId",
|
||||||
|
table: "Nominations");
|
||||||
|
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_Nominations_StreamerIdentities_StreamerIdentityId",
|
||||||
|
table: "Nominations");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "StreamerIdentities");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_Nominations_SeasonId_CategoryGroupName_Status",
|
||||||
|
table: "Nominations");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_Nominations_SeasonId_StreamerIdentityId_CategoryGroupName",
|
||||||
|
table: "Nominations");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_Nominations_StreamerIdentityId",
|
||||||
|
table: "Nominations");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_Nominations_SuggestedCategoryId",
|
||||||
|
table: "Nominations");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_Candidates_StreamerIdentityId",
|
||||||
|
table: "Candidates");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "AvgViewers",
|
||||||
|
table: "Nominations");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "CategoryGroupName",
|
||||||
|
table: "Nominations");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "ResolvedChannel",
|
||||||
|
table: "Nominations");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "ResolvedPlatform",
|
||||||
|
table: "Nominations");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "StreamerIdentityId",
|
||||||
|
table: "Nominations");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "SuggestedCategoryId",
|
||||||
|
table: "Nominations");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "TrackerCheckedAt",
|
||||||
|
table: "Nominations");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "TrackerStatus",
|
||||||
|
table: "Nominations");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "NominationTally",
|
||||||
|
table: "Candidates");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "StreamerIdentityId",
|
||||||
|
table: "Candidates");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<int>(
|
||||||
|
name: "CategoryId",
|
||||||
|
table: "Nominations",
|
||||||
|
type: "integer",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0,
|
||||||
|
oldClrType: typeof(int),
|
||||||
|
oldType: "integer",
|
||||||
|
oldNullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_Nominations_Categories_CategoryId",
|
||||||
|
table: "Nominations",
|
||||||
|
column: "CategoryId",
|
||||||
|
principalTable: "Categories",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1942
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,46 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddShowactApplicationSchedule : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<DateOnly>(
|
||||||
|
name: "ShowactApplicationEndsAt",
|
||||||
|
table: "SiteSettings",
|
||||||
|
type: "date",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<DateOnly>(
|
||||||
|
name: "ShowactApplicationStartsAt",
|
||||||
|
table: "SiteSettings",
|
||||||
|
type: "date",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "SiteSettings",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 1,
|
||||||
|
columns: new[] { "ShowactApplicationEndsAt", "ShowactApplicationStartsAt" },
|
||||||
|
values: new object[] { null, null });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "ShowactApplicationEndsAt",
|
||||||
|
table: "SiteSettings");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "ShowactApplicationStartsAt",
|
||||||
|
table: "SiteSettings");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,36 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddSeasonWorkflowRulesJson : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "WorkflowRulesJson",
|
||||||
|
table: "Seasons",
|
||||||
|
type: "text",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "[]");
|
||||||
|
|
||||||
|
migrationBuilder.Sql(
|
||||||
|
"""
|
||||||
|
UPDATE "Seasons"
|
||||||
|
SET "WorkflowRulesJson" = COALESCE(NULLIF((SELECT "WorkflowRulesJson" FROM "SiteSettings" WHERE "Id" = 1), ''), '[]')
|
||||||
|
WHERE COALESCE("WorkflowRulesJson", '') = '';
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "WorkflowRulesJson",
|
||||||
|
table: "Seasons");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,795 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Backend.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class InitialCleanBaseline : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "AdminAuditEntries",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "integer", nullable: false)
|
|
||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
|
||||||
AdminTwitchUserId = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
|
||||||
ActionType = table.Column<string>(type: "character varying(80)", maxLength: 80, nullable: false),
|
|
||||||
EntityType = table.Column<string>(type: "character varying(80)", maxLength: 80, nullable: false),
|
|
||||||
EntityId = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
|
||||||
Summary = table.Column<string>(type: "character varying(240)", maxLength: 240, nullable: false),
|
|
||||||
MetadataJson = table.Column<string>(type: "text", nullable: false),
|
|
||||||
CreatedFromIp = table.Column<string>(type: "character varying(80)", maxLength: 80, nullable: false),
|
|
||||||
UserAgent = table.Column<string>(type: "character varying(400)", maxLength: 400, nullable: false),
|
|
||||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_AdminAuditEntries", x => x.Id);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "Seasons",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "integer", nullable: false)
|
|
||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
|
||||||
Year = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
Name = table.Column<string>(type: "character varying(160)", maxLength: 160, nullable: false),
|
|
||||||
IsDemo = table.Column<bool>(type: "boolean", nullable: false, defaultValue: false),
|
|
||||||
IsCurrent = table.Column<bool>(type: "boolean", nullable: false),
|
|
||||||
IsCommunityOnly = table.Column<bool>(type: "boolean", nullable: false),
|
|
||||||
CurrentPhase = table.Column<string>(type: "character varying(60)", maxLength: 60, nullable: false),
|
|
||||||
NominationStartsAt = table.Column<DateOnly>(type: "date", nullable: false),
|
|
||||||
NominationEndsAt = table.Column<DateOnly>(type: "date", nullable: false),
|
|
||||||
VotingStartsAt = table.Column<DateOnly>(type: "date", nullable: false),
|
|
||||||
VotingEndsAt = table.Column<DateOnly>(type: "date", nullable: false),
|
|
||||||
ReviewStartsAt = table.Column<DateOnly>(type: "date", nullable: false),
|
|
||||||
ReviewEndsAt = table.Column<DateOnly>(type: "date", nullable: false),
|
|
||||||
ShowDate = table.Column<DateOnly>(type: "date", nullable: false),
|
|
||||||
ShowStartsAt = table.Column<TimeOnly>(type: "time without time zone", nullable: false),
|
|
||||||
WinnersPublishedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
|
||||||
WinnersPublishedByTwitchId = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: true),
|
|
||||||
SubcategoryTemplatesJson = table.Column<string>(type: "text", nullable: false, defaultValue: "[]"),
|
|
||||||
WorkflowRulesJson = table.Column<string>(type: "text", nullable: false, defaultValue: "[]")
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_Seasons", x => x.Id);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "SiteSettings",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "integer", nullable: false)
|
|
||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
|
||||||
HostDisplayName = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
|
||||||
HostTagline = table.Column<string>(type: "character varying(160)", maxLength: 160, nullable: false),
|
|
||||||
NewsletterUrl = table.Column<string>(type: "character varying(400)", maxLength: 400, nullable: false),
|
|
||||||
ShareXUrl = table.Column<string>(type: "text", nullable: false),
|
|
||||||
ShareDiscordUrl = table.Column<string>(type: "text", nullable: false),
|
|
||||||
PrivacyEmail = table.Column<string>(type: "character varying(160)", maxLength: 160, nullable: false),
|
|
||||||
PrivacyPolicyContent = table.Column<string>(type: "text", nullable: false),
|
|
||||||
PrivacyPolicyUpdatedBy = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: true),
|
|
||||||
PrivacyPolicyUpdatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
|
||||||
ImprintUrl = table.Column<string>(type: "character varying(400)", maxLength: 400, nullable: false),
|
|
||||||
ImprintContent = table.Column<string>(type: "text", nullable: false),
|
|
||||||
ContactUrl = table.Column<string>(type: "character varying(400)", maxLength: 400, nullable: false),
|
|
||||||
ContactContent = table.Column<string>(type: "text", nullable: false),
|
|
||||||
SponsorsUrl = table.Column<string>(type: "character varying(400)", maxLength: 400, nullable: false),
|
|
||||||
SponsorsContent = table.Column<string>(type: "text", nullable: false),
|
|
||||||
ShowactsUrl = table.Column<string>(type: "character varying(400)", maxLength: 400, nullable: false),
|
|
||||||
ShowactsContent = table.Column<string>(type: "text", nullable: false, defaultValue: ""),
|
|
||||||
StreamBannerEyebrow = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
|
||||||
StreamBannerTitle = table.Column<string>(type: "character varying(160)", maxLength: 160, nullable: false),
|
|
||||||
StreamBannerText = table.Column<string>(type: "text", nullable: false),
|
|
||||||
StreamBannerLiveButtonLabel = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
|
||||||
StreamBannerLiveButtonUrl = table.Column<string>(type: "character varying(400)", maxLength: 400, nullable: false),
|
|
||||||
StreamBannerLockedButtonLabel = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
|
||||||
StreamBannerUseCompletedContent = table.Column<bool>(type: "boolean", nullable: false),
|
|
||||||
StreamBannerCompletedEyebrow = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
|
||||||
StreamBannerCompletedTitle = table.Column<string>(type: "character varying(160)", maxLength: 160, nullable: false),
|
|
||||||
StreamBannerCompletedText = table.Column<string>(type: "text", nullable: false),
|
|
||||||
StreamBannerCompletedButtonLabel = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
|
||||||
StreamBannerCompletedButtonUrl = table.Column<string>(type: "character varying(400)", maxLength: 400, nullable: false),
|
|
||||||
AwardsSectionTitle = table.Column<string>(type: "text", nullable: false),
|
|
||||||
AwardsSectionDescription = table.Column<string>(type: "text", nullable: false),
|
|
||||||
SubcategoriesSectionTitle = table.Column<string>(type: "text", nullable: false),
|
|
||||||
SubcategoriesSectionDescription = table.Column<string>(type: "text", nullable: false),
|
|
||||||
SocialLinksJson = table.Column<string>(type: "text", nullable: false),
|
|
||||||
FaqJson = table.Column<string>(type: "text", nullable: false),
|
|
||||||
RiskRulesJson = table.Column<string>(type: "text", nullable: false),
|
|
||||||
WorkflowRulesJson = table.Column<string>(type: "text", nullable: false, defaultValue: "[]"),
|
|
||||||
TrackingRulesJson = table.Column<string>(type: "text", nullable: false, defaultValue: "[]"),
|
|
||||||
ViewerStatsProviderBaseUrl = table.Column<string>(type: "character varying(400)", maxLength: 400, nullable: false),
|
|
||||||
TrackingReviewNotes = table.Column<string>(type: "text", nullable: false),
|
|
||||||
NominationLinkBlacklistJson = table.Column<string>(type: "text", nullable: false, defaultValue: "[]"),
|
|
||||||
ClipSubmissionsEnabled = table.Column<bool>(type: "boolean", nullable: false, defaultValue: false),
|
|
||||||
ClipReviewEnabled = table.Column<bool>(type: "boolean", nullable: false, defaultValue: true),
|
|
||||||
ClipAdminMenuVisible = table.Column<bool>(type: "boolean", nullable: false),
|
|
||||||
ClipSubmissionDisabledMessage = table.Column<string>(type: "character varying(240)", maxLength: 240, nullable: false),
|
|
||||||
ShowactApplicationsEnabled = table.Column<bool>(type: "boolean", nullable: false, defaultValue: false),
|
|
||||||
ShowactApplicationStartsAt = table.Column<DateOnly>(type: "date", nullable: true),
|
|
||||||
ShowactApplicationEndsAt = table.Column<DateOnly>(type: "date", nullable: true),
|
|
||||||
ShowactApplicationDisabledMessage = table.Column<string>(type: "character varying(240)", maxLength: 240, nullable: false),
|
|
||||||
ShowactFormSchemaJson = table.Column<string>(type: "text", nullable: false),
|
|
||||||
SponsorsVisible = table.Column<bool>(type: "boolean", nullable: false, defaultValue: true),
|
|
||||||
DemoLoginManagedByDatabase = table.Column<bool>(type: "boolean", nullable: false),
|
|
||||||
DemoLoginEnabled = table.Column<bool>(type: "boolean", nullable: false),
|
|
||||||
DemoLoginEmail = table.Column<string>(type: "character varying(180)", maxLength: 180, nullable: false),
|
|
||||||
DemoLoginPasswordHash = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
|
||||||
DemoLoginPasswordSalt = table.Column<string>(type: "character varying(80)", maxLength: 80, nullable: false),
|
|
||||||
DemoLoginTwitchUserId = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
|
||||||
DemoLoginDisplayName = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
|
||||||
TwitchAuthManagedByDatabase = table.Column<bool>(type: "boolean", nullable: false),
|
|
||||||
TwitchClientId = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
|
||||||
TwitchClientSecret = table.Column<string>(type: "character varying(180)", maxLength: 180, nullable: false),
|
|
||||||
TwitchRedirectUri = table.Column<string>(type: "character varying(400)", maxLength: 400, nullable: false),
|
|
||||||
TwitchScope = table.Column<string>(type: "character varying(300)", maxLength: 300, nullable: false),
|
|
||||||
SessionIdleTimeoutHours = table.Column<int>(type: "integer", nullable: false, defaultValue: 3),
|
|
||||||
MaintenanceModeEnabled = table.Column<bool>(type: "boolean", nullable: false),
|
|
||||||
MaintenanceTitle = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
|
||||||
MaintenanceMessage = table.Column<string>(type: "character varying(600)", maxLength: 600, nullable: false)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_SiteSettings", x => x.Id);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "StreamerIdentities",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "integer", nullable: false)
|
|
||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
|
||||||
Platform = table.Column<string>(type: "character varying(40)", maxLength: 40, nullable: false),
|
|
||||||
Login = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
|
||||||
NormalizedKey = table.Column<string>(type: "character varying(180)", maxLength: 180, nullable: false),
|
|
||||||
DisplayName = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
|
||||||
ProfileUrl = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
|
|
||||||
LastResolvedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_StreamerIdentities", x => x.Id);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "TeamMembers",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "integer", nullable: false)
|
|
||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
|
||||||
Login = table.Column<string>(type: "character varying(80)", maxLength: 80, nullable: false),
|
|
||||||
DisplayName = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
|
||||||
Role = table.Column<string>(type: "character varying(40)", maxLength: 40, nullable: false),
|
|
||||||
PasswordHash = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
|
||||||
PasswordSalt = table.Column<string>(type: "character varying(80)", maxLength: 80, nullable: false),
|
|
||||||
BoundTwitchUserId = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: true),
|
|
||||||
BoundTwitchDisplayName = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: true),
|
|
||||||
MustChangePassword = table.Column<bool>(type: "boolean", nullable: false),
|
|
||||||
IsActive = table.Column<bool>(type: "boolean", nullable: false),
|
|
||||||
CreatedByTwitchId = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
|
||||||
UpdatedByTwitchId = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: true),
|
|
||||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
|
||||||
UpdatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
|
||||||
LastLoginAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
|
||||||
TwitchBoundAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
|
||||||
PasswordResetAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_TeamMembers", x => x.Id);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "TeamRolePermissions",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "integer", nullable: false)
|
|
||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
|
||||||
Role = table.Column<string>(type: "character varying(40)", maxLength: 40, nullable: false),
|
|
||||||
PermissionsJson = table.Column<string>(type: "text", nullable: false),
|
|
||||||
UpdatedByTwitchId = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
|
||||||
UpdatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_TeamRolePermissions", x => x.Id);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "UserSessions",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
|
||||||
SessionToken = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
|
||||||
TwitchUserId = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
|
||||||
DisplayName = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
|
||||||
Role = table.Column<string>(type: "character varying(40)", maxLength: 40, nullable: false),
|
|
||||||
CreatedFromIp = table.Column<string>(type: "character varying(80)", maxLength: 80, nullable: false),
|
|
||||||
UserAgent = table.Column<string>(type: "character varying(400)", maxLength: 400, nullable: false),
|
|
||||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
|
||||||
LastSeenAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
|
||||||
IsActive = table.Column<bool>(type: "boolean", nullable: false)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_UserSessions", x => x.Id);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "Categories",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "integer", nullable: false)
|
|
||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
|
||||||
SeasonId = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
GroupName = table.Column<string>(type: "character varying(80)", maxLength: 80, nullable: false),
|
|
||||||
Name = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
|
||||||
Slug = table.Column<string>(type: "text", nullable: false),
|
|
||||||
Description = table.Column<string>(type: "character varying(400)", maxLength: 400, nullable: false),
|
|
||||||
SortOrder = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
MaxNomineesPerUser = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
ViewerRangeMin = table.Column<int>(type: "integer", nullable: true),
|
|
||||||
ViewerRangeMax = table.Column<int>(type: "integer", nullable: true)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_Categories", x => x.Id);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_Categories_Seasons_SeasonId",
|
|
||||||
column: x => x.SeasonId,
|
|
||||||
principalTable: "Seasons",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "RiskFlags",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "integer", nullable: false)
|
|
||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
|
||||||
SeasonId = table.Column<int>(type: "integer", nullable: true),
|
|
||||||
TwitchUserId = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: true),
|
|
||||||
Source = table.Column<string>(type: "character varying(80)", maxLength: 80, nullable: false),
|
|
||||||
Type = table.Column<string>(type: "character varying(80)", maxLength: 80, nullable: false),
|
|
||||||
Severity = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
|
|
||||||
Status = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
|
|
||||||
Summary = table.Column<string>(type: "character varying(240)", maxLength: 240, nullable: false),
|
|
||||||
CreatedFromIp = table.Column<string>(type: "character varying(80)", maxLength: 80, nullable: false),
|
|
||||||
UserAgent = table.Column<string>(type: "character varying(400)", maxLength: 400, nullable: false),
|
|
||||||
MetadataJson = table.Column<string>(type: "text", nullable: false),
|
|
||||||
ReviewNote = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
|
|
||||||
ReviewedByTwitchId = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: true),
|
|
||||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
|
||||||
ReviewedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_RiskFlags", x => x.Id);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_RiskFlags_Seasons_SeasonId",
|
|
||||||
column: x => x.SeasonId,
|
|
||||||
principalTable: "Seasons",
|
|
||||||
principalColumn: "Id");
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "ShowactApplications",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "integer", nullable: false)
|
|
||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
|
||||||
SeasonId = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
ArtistName = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
|
||||||
ContactEmail = table.Column<string>(type: "character varying(180)", maxLength: 180, nullable: false),
|
|
||||||
ContactDiscord = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
|
||||||
PlatformUrl = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: false),
|
|
||||||
PerformanceType = table.Column<string>(type: "character varying(80)", maxLength: 80, nullable: false),
|
|
||||||
Description = table.Column<string>(type: "character varying(1000)", maxLength: 1000, nullable: false),
|
|
||||||
TechnicalNotes = table.Column<string>(type: "character varying(1000)", maxLength: 1000, nullable: false),
|
|
||||||
ReferenceUrl = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: false),
|
|
||||||
FieldResponsesJson = table.Column<string>(type: "text", nullable: false),
|
|
||||||
Status = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
|
|
||||||
ReviewNote = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
|
|
||||||
ReviewedByTwitchId = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: true),
|
|
||||||
CreatedFromIp = table.Column<string>(type: "character varying(80)", maxLength: 80, nullable: false),
|
|
||||||
UserAgent = table.Column<string>(type: "character varying(400)", maxLength: 400, nullable: false),
|
|
||||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
|
||||||
ReviewedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_ShowactApplications", x => x.Id);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_ShowactApplications_Seasons_SeasonId",
|
|
||||||
column: x => x.SeasonId,
|
|
||||||
principalTable: "Seasons",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "Sponsors",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "integer", nullable: false)
|
|
||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
|
||||||
SeasonId = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
Name = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
|
||||||
WebsiteUrl = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: false),
|
|
||||||
LogoUrl = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: false),
|
|
||||||
Description = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: false),
|
|
||||||
Tier = table.Column<string>(type: "character varying(80)", maxLength: 80, nullable: false),
|
|
||||||
SortOrder = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
IsVisible = table.Column<bool>(type: "boolean", nullable: false),
|
|
||||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
|
||||||
UpdatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_Sponsors", x => x.Id);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_Sponsors_Seasons_SeasonId",
|
|
||||||
column: x => x.SeasonId,
|
|
||||||
principalTable: "Seasons",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "VoteBallots",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "integer", nullable: false)
|
|
||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
|
||||||
SeasonId = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
SubmittedByTwitchId = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
|
||||||
Status = table.Column<string>(type: "character varying(30)", maxLength: 30, nullable: false),
|
|
||||||
SubmittedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_VoteBallots", x => x.Id);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_VoteBallots_Seasons_SeasonId",
|
|
||||||
column: x => x.SeasonId,
|
|
||||||
principalTable: "Seasons",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "Candidates",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "integer", nullable: false)
|
|
||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
|
||||||
SeasonId = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
CategoryId = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
StreamerIdentityId = table.Column<int>(type: "integer", nullable: true),
|
|
||||||
DisplayName = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
|
||||||
ChannelSlug = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
|
||||||
Platform = table.Column<string>(type: "character varying(40)", maxLength: 40, nullable: false),
|
|
||||||
NominationTally = table.Column<int>(type: "integer", nullable: false, defaultValue: 0),
|
|
||||||
AcceptanceStatus = table.Column<string>(type: "character varying(30)", maxLength: 30, nullable: false, defaultValue: "open"),
|
|
||||||
AcceptanceNote = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
|
|
||||||
ClipCompilationUrl = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
|
|
||||||
ClipCompilationTitle = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
|
|
||||||
ClipCompilationPlatform = table.Column<string>(type: "character varying(40)", maxLength: 40, nullable: true),
|
|
||||||
ClipEmbedStatus = table.Column<string>(type: "character varying(30)", maxLength: 30, nullable: false, defaultValue: "unchecked")
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_Candidates", x => x.Id);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_Candidates_Categories_CategoryId",
|
|
||||||
column: x => x.CategoryId,
|
|
||||||
principalTable: "Categories",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_Candidates_Seasons_SeasonId",
|
|
||||||
column: x => x.SeasonId,
|
|
||||||
principalTable: "Seasons",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_Candidates_StreamerIdentities_StreamerIdentityId",
|
|
||||||
column: x => x.StreamerIdentityId,
|
|
||||||
principalTable: "StreamerIdentities",
|
|
||||||
principalColumn: "Id");
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "ClipSubmissions",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "integer", nullable: false)
|
|
||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
|
||||||
SeasonId = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
CategoryId = table.Column<int>(type: "integer", nullable: true),
|
|
||||||
CandidateId = table.Column<int>(type: "integer", nullable: true),
|
|
||||||
SubmittedByTwitchId = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
|
||||||
ClipUrl = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: false),
|
|
||||||
Title = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
|
|
||||||
Creator = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
|
||||||
Platform = table.Column<string>(type: "character varying(40)", maxLength: 40, nullable: false),
|
|
||||||
Status = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
|
|
||||||
ReviewNote = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
|
|
||||||
ReviewedByTwitchId = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: true),
|
|
||||||
CreatedFromIp = table.Column<string>(type: "character varying(80)", maxLength: 80, nullable: false),
|
|
||||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
|
||||||
ReviewedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_ClipSubmissions", x => x.Id);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_ClipSubmissions_Candidates_CandidateId",
|
|
||||||
column: x => x.CandidateId,
|
|
||||||
principalTable: "Candidates",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.SetNull);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_ClipSubmissions_Seasons_SeasonId",
|
|
||||||
column: x => x.SeasonId,
|
|
||||||
principalTable: "Seasons",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "Nominations",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "integer", nullable: false)
|
|
||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
|
||||||
SeasonId = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
CategoryId = table.Column<int>(type: "integer", nullable: true),
|
|
||||||
CategoryGroupName = table.Column<string>(type: "character varying(80)", maxLength: 80, nullable: false, defaultValue: ""),
|
|
||||||
SubmittedByTwitchId = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
|
||||||
CandidateId = table.Column<int>(type: "integer", nullable: true),
|
|
||||||
StreamerIdentityId = table.Column<int>(type: "integer", nullable: true),
|
|
||||||
SuggestedCategoryId = table.Column<int>(type: "integer", nullable: true),
|
|
||||||
CandidateText = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: true),
|
|
||||||
StreamUrl = table.Column<string>(type: "character varying(300)", maxLength: 300, nullable: true),
|
|
||||||
ResolvedChannel = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: true),
|
|
||||||
ResolvedPlatform = table.Column<string>(type: "character varying(40)", maxLength: 40, nullable: true),
|
|
||||||
AvgViewers = table.Column<int>(type: "integer", nullable: true),
|
|
||||||
HoursStreamed = table.Column<int>(type: "integer", nullable: true),
|
|
||||||
HoursWatched = table.Column<int>(type: "integer", nullable: true),
|
|
||||||
PeakViewers = table.Column<int>(type: "integer", nullable: true),
|
|
||||||
FollowersGained = table.Column<int>(type: "integer", nullable: true),
|
|
||||||
TrackerStatus = table.Column<string>(type: "character varying(40)", maxLength: 40, nullable: false, defaultValue: "pending"),
|
|
||||||
TrackerCheckedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
|
||||||
TrackingReviewStatus = table.Column<string>(type: "character varying(30)", maxLength: 30, nullable: false, defaultValue: "clear"),
|
|
||||||
TrackingFlagsJson = table.Column<string>(type: "text", nullable: false, defaultValue: "[]"),
|
|
||||||
TrackingReviewNote = table.Column<string>(type: "character varying(1000)", maxLength: 1000, nullable: true),
|
|
||||||
TrackingReviewedByTwitchId = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: true),
|
|
||||||
TrackingReviewedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
|
||||||
Status = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
|
|
||||||
ReviewNote = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
|
|
||||||
ReviewedByTwitchId = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: true),
|
|
||||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
|
||||||
ReviewedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_Nominations", x => x.Id);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_Nominations_Candidates_CandidateId",
|
|
||||||
column: x => x.CandidateId,
|
|
||||||
principalTable: "Candidates",
|
|
||||||
principalColumn: "Id");
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_Nominations_Categories_CategoryId",
|
|
||||||
column: x => x.CategoryId,
|
|
||||||
principalTable: "Categories",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.SetNull);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_Nominations_Categories_SuggestedCategoryId",
|
|
||||||
column: x => x.SuggestedCategoryId,
|
|
||||||
principalTable: "Categories",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.SetNull);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_Nominations_Seasons_SeasonId",
|
|
||||||
column: x => x.SeasonId,
|
|
||||||
principalTable: "Seasons",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_Nominations_StreamerIdentities_StreamerIdentityId",
|
|
||||||
column: x => x.StreamerIdentityId,
|
|
||||||
principalTable: "StreamerIdentities",
|
|
||||||
principalColumn: "Id");
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "Results",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "integer", nullable: false)
|
|
||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
|
||||||
SeasonId = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
CategoryId = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
CandidateId = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
CategoryName = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_Results", x => x.Id);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_Results_Candidates_CandidateId",
|
|
||||||
column: x => x.CandidateId,
|
|
||||||
principalTable: "Candidates",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_Results_Categories_CategoryId",
|
|
||||||
column: x => x.CategoryId,
|
|
||||||
principalTable: "Categories",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_Results_Seasons_SeasonId",
|
|
||||||
column: x => x.SeasonId,
|
|
||||||
principalTable: "Seasons",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "VoteEntries",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "integer", nullable: false)
|
|
||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
|
||||||
BallotId = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
CategoryId = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
CandidateId = table.Column<int>(type: "integer", nullable: false)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_VoteEntries", x => x.Id);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_VoteEntries_Candidates_CandidateId",
|
|
||||||
column: x => x.CandidateId,
|
|
||||||
principalTable: "Candidates",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_VoteEntries_Categories_CategoryId",
|
|
||||||
column: x => x.CategoryId,
|
|
||||||
principalTable: "Categories",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_VoteEntries_VoteBallots_BallotId",
|
|
||||||
column: x => x.BallotId,
|
|
||||||
principalTable: "VoteBallots",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_Candidates_CategoryId",
|
|
||||||
table: "Candidates",
|
|
||||||
column: "CategoryId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_Candidates_SeasonId",
|
|
||||||
table: "Candidates",
|
|
||||||
column: "SeasonId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_Candidates_StreamerIdentityId",
|
|
||||||
table: "Candidates",
|
|
||||||
column: "StreamerIdentityId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_Categories_SeasonId_Slug",
|
|
||||||
table: "Categories",
|
|
||||||
columns: new[] { "SeasonId", "Slug" },
|
|
||||||
unique: true);
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_ClipSubmissions_CandidateId",
|
|
||||||
table: "ClipSubmissions",
|
|
||||||
column: "CandidateId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_ClipSubmissions_SeasonId_Status",
|
|
||||||
table: "ClipSubmissions",
|
|
||||||
columns: new[] { "SeasonId", "Status" });
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_Nominations_CandidateId",
|
|
||||||
table: "Nominations",
|
|
||||||
column: "CandidateId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_Nominations_CategoryId",
|
|
||||||
table: "Nominations",
|
|
||||||
column: "CategoryId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_Nominations_SeasonId_CategoryGroupName_Status",
|
|
||||||
table: "Nominations",
|
|
||||||
columns: new[] { "SeasonId", "CategoryGroupName", "Status" });
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_Nominations_SeasonId_Status",
|
|
||||||
table: "Nominations",
|
|
||||||
columns: new[] { "SeasonId", "Status" });
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_Nominations_SeasonId_StreamerIdentityId_CategoryGroupName",
|
|
||||||
table: "Nominations",
|
|
||||||
columns: new[] { "SeasonId", "StreamerIdentityId", "CategoryGroupName" });
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_Nominations_StreamerIdentityId",
|
|
||||||
table: "Nominations",
|
|
||||||
column: "StreamerIdentityId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_Nominations_SuggestedCategoryId",
|
|
||||||
table: "Nominations",
|
|
||||||
column: "SuggestedCategoryId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_Results_CandidateId",
|
|
||||||
table: "Results",
|
|
||||||
column: "CandidateId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_Results_CategoryId",
|
|
||||||
table: "Results",
|
|
||||||
column: "CategoryId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_Results_SeasonId_CategoryId",
|
|
||||||
table: "Results",
|
|
||||||
columns: new[] { "SeasonId", "CategoryId" },
|
|
||||||
unique: true);
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_RiskFlags_SeasonId",
|
|
||||||
table: "RiskFlags",
|
|
||||||
column: "SeasonId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_Seasons_Year",
|
|
||||||
table: "Seasons",
|
|
||||||
column: "Year",
|
|
||||||
unique: true);
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_ShowactApplications_SeasonId_Status",
|
|
||||||
table: "ShowactApplications",
|
|
||||||
columns: new[] { "SeasonId", "Status" });
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_Sponsors_SeasonId_IsVisible_SortOrder",
|
|
||||||
table: "Sponsors",
|
|
||||||
columns: new[] { "SeasonId", "IsVisible", "SortOrder" });
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_StreamerIdentities_NormalizedKey",
|
|
||||||
table: "StreamerIdentities",
|
|
||||||
column: "NormalizedKey",
|
|
||||||
unique: true);
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_TeamMembers_BoundTwitchUserId",
|
|
||||||
table: "TeamMembers",
|
|
||||||
column: "BoundTwitchUserId",
|
|
||||||
unique: true);
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_TeamMembers_Login",
|
|
||||||
table: "TeamMembers",
|
|
||||||
column: "Login",
|
|
||||||
unique: true);
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_TeamRolePermissions_Role",
|
|
||||||
table: "TeamRolePermissions",
|
|
||||||
column: "Role",
|
|
||||||
unique: true);
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_UserSessions_SessionToken",
|
|
||||||
table: "UserSessions",
|
|
||||||
column: "SessionToken",
|
|
||||||
unique: true);
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_VoteBallots_SeasonId_SubmittedByTwitchId",
|
|
||||||
table: "VoteBallots",
|
|
||||||
columns: new[] { "SeasonId", "SubmittedByTwitchId" },
|
|
||||||
unique: true);
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_VoteEntries_BallotId",
|
|
||||||
table: "VoteEntries",
|
|
||||||
column: "BallotId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_VoteEntries_CandidateId",
|
|
||||||
table: "VoteEntries",
|
|
||||||
column: "CandidateId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_VoteEntries_CategoryId",
|
|
||||||
table: "VoteEntries",
|
|
||||||
column: "CategoryId");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "AdminAuditEntries");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "ClipSubmissions");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "Nominations");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "Results");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "RiskFlags");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "ShowactApplications");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "SiteSettings");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "Sponsors");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "TeamMembers");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "TeamRolePermissions");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "UserSessions");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "VoteEntries");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "Candidates");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "VoteBallots");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "Categories");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "StreamerIdentities");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "Seasons");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+2
-2
@@ -12,8 +12,8 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|||||||
namespace Backend.Migrations
|
namespace Backend.Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof(AwardsDbContext))]
|
[DbContext(typeof(AwardsDbContext))]
|
||||||
[Migration("20260629142009_InitialCleanBaseline")]
|
[Migration("20260629142100_AddLandingContentSections")]
|
||||||
partial class InitialCleanBaseline
|
partial class AddLandingContentSections
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddLandingContentSections : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql(
|
||||||
|
"""
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "StreamBannerEyebrow" character varying(120) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "StreamBannerTitle" character varying(160) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "StreamBannerText" text NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "StreamBannerLiveButtonLabel" character varying(120) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "StreamBannerLiveButtonUrl" character varying(400) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "StreamBannerLockedButtonLabel" character varying(120) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "StreamBannerUseCompletedContent" boolean NOT NULL DEFAULT FALSE;
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "StreamBannerCompletedEyebrow" character varying(120) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "StreamBannerCompletedTitle" character varying(160) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "StreamBannerCompletedText" text NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "StreamBannerCompletedButtonLabel" character varying(120) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "StreamBannerCompletedButtonUrl" character varying(400) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "AwardsSectionTitle" text NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "AwardsSectionDescription" text NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "SubcategoriesSectionTitle" text NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "SubcategoriesSectionDescription" text NOT NULL DEFAULT '';
|
||||||
|
"""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql(
|
||||||
|
"""
|
||||||
|
ALTER TABLE "SiteSettings" DROP COLUMN IF EXISTS "SubcategoriesSectionDescription";
|
||||||
|
ALTER TABLE "SiteSettings" DROP COLUMN IF EXISTS "SubcategoriesSectionTitle";
|
||||||
|
ALTER TABLE "SiteSettings" DROP COLUMN IF EXISTS "AwardsSectionDescription";
|
||||||
|
ALTER TABLE "SiteSettings" DROP COLUMN IF EXISTS "AwardsSectionTitle";
|
||||||
|
ALTER TABLE "SiteSettings" DROP COLUMN IF EXISTS "StreamBannerCompletedButtonUrl";
|
||||||
|
ALTER TABLE "SiteSettings" DROP COLUMN IF EXISTS "StreamBannerCompletedButtonLabel";
|
||||||
|
ALTER TABLE "SiteSettings" DROP COLUMN IF EXISTS "StreamBannerCompletedText";
|
||||||
|
ALTER TABLE "SiteSettings" DROP COLUMN IF EXISTS "StreamBannerCompletedTitle";
|
||||||
|
ALTER TABLE "SiteSettings" DROP COLUMN IF EXISTS "StreamBannerCompletedEyebrow";
|
||||||
|
ALTER TABLE "SiteSettings" DROP COLUMN IF EXISTS "StreamBannerUseCompletedContent";
|
||||||
|
ALTER TABLE "SiteSettings" DROP COLUMN IF EXISTS "StreamBannerLockedButtonLabel";
|
||||||
|
ALTER TABLE "SiteSettings" DROP COLUMN IF EXISTS "StreamBannerLiveButtonUrl";
|
||||||
|
ALTER TABLE "SiteSettings" DROP COLUMN IF EXISTS "StreamBannerLiveButtonLabel";
|
||||||
|
ALTER TABLE "SiteSettings" DROP COLUMN IF EXISTS "StreamBannerText";
|
||||||
|
ALTER TABLE "SiteSettings" DROP COLUMN IF EXISTS "StreamBannerTitle";
|
||||||
|
ALTER TABLE "SiteSettings" DROP COLUMN IF EXISTS "StreamBannerEyebrow";
|
||||||
|
"""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
using Backend.Data;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations;
|
||||||
|
|
||||||
|
[DbContext(typeof(AwardsDbContext))]
|
||||||
|
[Migration("20260629142200_EnsureSiteSettingsSeedColumns")]
|
||||||
|
public partial class EnsureSiteSettingsSeedColumns : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql(
|
||||||
|
"""
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "HostDisplayName" character varying(120) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "HostTagline" character varying(160) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "NewsletterUrl" character varying(400) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "ShareXUrl" text NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "ShareDiscordUrl" text NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "PrivacyEmail" character varying(160) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "PrivacyPolicyContent" text NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "PrivacyPolicyUpdatedBy" character varying(120);
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "PrivacyPolicyUpdatedAt" timestamp with time zone;
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "ImprintUrl" character varying(400) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "ImprintContent" text NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "ContactUrl" character varying(400) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "ContactContent" text NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "SponsorsUrl" character varying(400) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "SponsorsContent" text NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "ShowactsUrl" character varying(400) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "ShowactsContent" text NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "StreamBannerEyebrow" character varying(120) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "StreamBannerTitle" character varying(160) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "StreamBannerText" text NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "StreamBannerLiveButtonLabel" character varying(120) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "StreamBannerLiveButtonUrl" character varying(400) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "StreamBannerLockedButtonLabel" character varying(120) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "StreamBannerUseCompletedContent" boolean NOT NULL DEFAULT FALSE;
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "StreamBannerCompletedEyebrow" character varying(120) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "StreamBannerCompletedTitle" character varying(160) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "StreamBannerCompletedText" text NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "StreamBannerCompletedButtonLabel" character varying(120) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "StreamBannerCompletedButtonUrl" character varying(400) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "AwardsSectionTitle" text NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "AwardsSectionDescription" text NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "SubcategoriesSectionTitle" text NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "SubcategoriesSectionDescription" text NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "SocialLinksJson" text NOT NULL DEFAULT '[]';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "FaqJson" text NOT NULL DEFAULT '[]';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "RiskRulesJson" text NOT NULL DEFAULT '[]';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "WorkflowRulesJson" text NOT NULL DEFAULT '[]';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "TrackingRulesJson" text NOT NULL DEFAULT '[]';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "ViewerStatsProviderBaseUrl" character varying(400) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "TrackingReviewNotes" text NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "NominationLinkBlacklistJson" text NOT NULL DEFAULT '[]';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "ClipSubmissionsEnabled" boolean NOT NULL DEFAULT FALSE;
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "ClipReviewEnabled" boolean NOT NULL DEFAULT TRUE;
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "ClipAdminMenuVisible" boolean NOT NULL DEFAULT TRUE;
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "ClipSubmissionDisabledMessage" character varying(240) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "ShowactApplicationsEnabled" boolean NOT NULL DEFAULT FALSE;
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "ShowactApplicationStartsAt" date;
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "ShowactApplicationEndsAt" date;
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "ShowactApplicationDisabledMessage" character varying(240) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "ShowactFormSchemaJson" text NOT NULL DEFAULT '[]';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "SponsorsVisible" boolean NOT NULL DEFAULT TRUE;
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "DemoLoginManagedByDatabase" boolean NOT NULL DEFAULT FALSE;
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "DemoLoginEnabled" boolean NOT NULL DEFAULT FALSE;
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "DemoLoginEmail" character varying(180) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "DemoLoginPasswordHash" character varying(120) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "DemoLoginPasswordSalt" character varying(80) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "DemoLoginTwitchUserId" character varying(120) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "DemoLoginDisplayName" character varying(120) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "TwitchAuthManagedByDatabase" boolean NOT NULL DEFAULT FALSE;
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "TwitchClientId" character varying(120) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "TwitchClientSecret" character varying(180) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "TwitchRedirectUri" character varying(400) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "TwitchScope" character varying(300) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "SessionIdleTimeoutHours" integer NOT NULL DEFAULT 3;
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "MaintenanceModeEnabled" boolean NOT NULL DEFAULT FALSE;
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "MaintenanceTitle" character varying(120) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "MaintenanceMessage" character varying(600) NOT NULL DEFAULT '';
|
||||||
|
"""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
using Backend.Data;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations;
|
||||||
|
|
||||||
|
[DbContext(typeof(AwardsDbContext))]
|
||||||
|
[Migration("20260629142746_EnsureDemoSeedSchema")]
|
||||||
|
public partial class EnsureDemoSeedSchema : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql(
|
||||||
|
"""
|
||||||
|
ALTER TABLE "Seasons" ADD COLUMN IF NOT EXISTS "IsDemo" boolean NOT NULL DEFAULT FALSE;
|
||||||
|
ALTER TABLE "Seasons" ADD COLUMN IF NOT EXISTS "ShowStartsAt" time without time zone NOT NULL DEFAULT TIME '20:00';
|
||||||
|
ALTER TABLE "Seasons" ADD COLUMN IF NOT EXISTS "WinnersPublishedAt" timestamp with time zone;
|
||||||
|
ALTER TABLE "Seasons" ADD COLUMN IF NOT EXISTS "WinnersPublishedByTwitchId" character varying(120);
|
||||||
|
ALTER TABLE "Seasons" ADD COLUMN IF NOT EXISTS "SubcategoryTemplatesJson" text NOT NULL DEFAULT '[]';
|
||||||
|
ALTER TABLE "Seasons" ADD COLUMN IF NOT EXISTS "WorkflowRulesJson" text NOT NULL DEFAULT '[]';
|
||||||
|
|
||||||
|
ALTER TABLE "Categories" ADD COLUMN IF NOT EXISTS "ViewerRangeMin" integer;
|
||||||
|
ALTER TABLE "Categories" ADD COLUMN IF NOT EXISTS "ViewerRangeMax" integer;
|
||||||
|
|
||||||
|
ALTER TABLE "Candidates" ADD COLUMN IF NOT EXISTS "StreamerIdentityId" integer;
|
||||||
|
ALTER TABLE "Candidates" ADD COLUMN IF NOT EXISTS "NominationTally" integer NOT NULL DEFAULT 0;
|
||||||
|
ALTER TABLE "Candidates" ADD COLUMN IF NOT EXISTS "AcceptanceStatus" character varying(30) NOT NULL DEFAULT 'open';
|
||||||
|
ALTER TABLE "Candidates" ADD COLUMN IF NOT EXISTS "AcceptanceNote" character varying(500);
|
||||||
|
ALTER TABLE "Candidates" ADD COLUMN IF NOT EXISTS "ClipCompilationUrl" character varying(500);
|
||||||
|
ALTER TABLE "Candidates" ADD COLUMN IF NOT EXISTS "ClipCompilationTitle" character varying(200);
|
||||||
|
ALTER TABLE "Candidates" ADD COLUMN IF NOT EXISTS "ClipCompilationPlatform" character varying(40);
|
||||||
|
ALTER TABLE "Candidates" ADD COLUMN IF NOT EXISTS "ClipEmbedStatus" character varying(30) NOT NULL DEFAULT 'unchecked';
|
||||||
|
|
||||||
|
ALTER TABLE "Nominations" ADD COLUMN IF NOT EXISTS "CategoryGroupName" character varying(80) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "Nominations" ADD COLUMN IF NOT EXISTS "StreamerIdentityId" integer;
|
||||||
|
ALTER TABLE "Nominations" ADD COLUMN IF NOT EXISTS "SuggestedCategoryId" integer;
|
||||||
|
ALTER TABLE "Nominations" ADD COLUMN IF NOT EXISTS "ResolvedChannel" character varying(120);
|
||||||
|
ALTER TABLE "Nominations" ADD COLUMN IF NOT EXISTS "ResolvedPlatform" character varying(40);
|
||||||
|
ALTER TABLE "Nominations" ADD COLUMN IF NOT EXISTS "AvgViewers" integer;
|
||||||
|
ALTER TABLE "Nominations" ADD COLUMN IF NOT EXISTS "HoursStreamed" integer;
|
||||||
|
ALTER TABLE "Nominations" ADD COLUMN IF NOT EXISTS "HoursWatched" integer;
|
||||||
|
ALTER TABLE "Nominations" ADD COLUMN IF NOT EXISTS "PeakViewers" integer;
|
||||||
|
ALTER TABLE "Nominations" ADD COLUMN IF NOT EXISTS "FollowersGained" integer;
|
||||||
|
ALTER TABLE "Nominations" ADD COLUMN IF NOT EXISTS "TrackerStatus" character varying(40) NOT NULL DEFAULT 'pending';
|
||||||
|
ALTER TABLE "Nominations" ADD COLUMN IF NOT EXISTS "TrackerCheckedAt" timestamp with time zone;
|
||||||
|
ALTER TABLE "Nominations" ADD COLUMN IF NOT EXISTS "TrackingReviewStatus" character varying(30) NOT NULL DEFAULT 'clear';
|
||||||
|
ALTER TABLE "Nominations" ADD COLUMN IF NOT EXISTS "TrackingFlagsJson" text NOT NULL DEFAULT '[]';
|
||||||
|
ALTER TABLE "Nominations" ADD COLUMN IF NOT EXISTS "TrackingReviewNote" character varying(1000);
|
||||||
|
ALTER TABLE "Nominations" ADD COLUMN IF NOT EXISTS "TrackingReviewedByTwitchId" character varying(120);
|
||||||
|
ALTER TABLE "Nominations" ADD COLUMN IF NOT EXISTS "TrackingReviewedAt" timestamp with time zone;
|
||||||
|
|
||||||
|
DO $vtsa_optional_demo_seed_schema$
|
||||||
|
BEGIN
|
||||||
|
IF to_regclass('"ClipSubmissions"') IS NOT NULL THEN
|
||||||
|
ALTER TABLE "ClipSubmissions" ADD COLUMN IF NOT EXISTS "CategoryId" integer;
|
||||||
|
ALTER TABLE "ClipSubmissions" ADD COLUMN IF NOT EXISTS "CandidateId" integer;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF to_regclass('"ShowactApplications"') IS NOT NULL THEN
|
||||||
|
ALTER TABLE "ShowactApplications" ADD COLUMN IF NOT EXISTS "FieldResponsesJson" text NOT NULL DEFAULT '{}';
|
||||||
|
END IF;
|
||||||
|
END;
|
||||||
|
$vtsa_optional_demo_seed_schema$;
|
||||||
|
"""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,13 @@ public partial class SeedRealisticDemoScenario : Migration
|
|||||||
{
|
{
|
||||||
migrationBuilder.Sql(
|
migrationBuilder.Sql(
|
||||||
"""
|
"""
|
||||||
|
DO $vtsa_seed$
|
||||||
|
BEGIN
|
||||||
|
IF EXISTS (SELECT 1 FROM "Seasons" WHERE "Year" IN (2025, 2026)) THEN
|
||||||
|
RAISE NOTICE 'Existing seasons detected; skipping realistic demo seed.';
|
||||||
|
RETURN;
|
||||||
|
END IF;
|
||||||
|
|
||||||
INSERT INTO "Seasons" (
|
INSERT INTO "Seasons" (
|
||||||
"Id", "Year", "Name", "IsDemo", "IsCurrent", "IsCommunityOnly", "CurrentPhase",
|
"Id", "Year", "Name", "IsDemo", "IsCurrent", "IsCommunityOnly", "CurrentPhase",
|
||||||
"NominationStartsAt", "NominationEndsAt", "VotingStartsAt", "VotingEndsAt",
|
"NominationStartsAt", "NominationEndsAt", "VotingStartsAt", "VotingEndsAt",
|
||||||
@@ -240,6 +247,8 @@ public partial class SeedRealisticDemoScenario : Migration
|
|||||||
SELECT setval(pg_get_serial_sequence('"ShowactApplications"', 'Id'), COALESCE((SELECT MAX("Id") FROM "ShowactApplications"), 1));
|
SELECT setval(pg_get_serial_sequence('"ShowactApplications"', 'Id'), COALESCE((SELECT MAX("Id") FROM "ShowactApplications"), 1));
|
||||||
SELECT setval(pg_get_serial_sequence('"Sponsors"', 'Id'), COALESCE((SELECT MAX("Id") FROM "Sponsors"), 1));
|
SELECT setval(pg_get_serial_sequence('"Sponsors"', 'Id'), COALESCE((SELECT MAX("Id") FROM "Sponsors"), 1));
|
||||||
SELECT setval(pg_get_serial_sequence('"RiskFlags"', 'Id'), COALESCE((SELECT MAX("Id") FROM "RiskFlags"), 1));
|
SELECT setval(pg_get_serial_sequence('"RiskFlags"', 'Id'), COALESCE((SELECT MAX("Id") FROM "RiskFlags"), 1));
|
||||||
|
END;
|
||||||
|
$vtsa_seed$;
|
||||||
"""
|
"""
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,18 @@ public partial class AdjustDemoSubcategoriesAndArchive : Migration
|
|||||||
{
|
{
|
||||||
migrationBuilder.Sql(
|
migrationBuilder.Sql(
|
||||||
"""
|
"""
|
||||||
|
DO $vtsa_adjust$
|
||||||
|
BEGIN
|
||||||
|
IF EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM "Seasons"
|
||||||
|
WHERE "Year" IN (2024, 2025, 2026)
|
||||||
|
AND "Id" NOT IN (999, 1000, 1001)
|
||||||
|
) THEN
|
||||||
|
RAISE NOTICE 'Existing non-demo season years detected; skipping demo archive reseed.';
|
||||||
|
RETURN;
|
||||||
|
END IF;
|
||||||
|
|
||||||
DELETE FROM "RiskFlags" WHERE "SeasonId" IN (999, 1000, 1001);
|
DELETE FROM "RiskFlags" WHERE "SeasonId" IN (999, 1000, 1001);
|
||||||
DELETE FROM "Seasons" WHERE "Id" IN (999, 1000, 1001) AND "IsDemo" = TRUE;
|
DELETE FROM "Seasons" WHERE "Id" IN (999, 1000, 1001) AND "IsDemo" = TRUE;
|
||||||
DELETE FROM "StreamerIdentities" WHERE "Id" BETWEEN 3001 AND 3020;
|
DELETE FROM "StreamerIdentities" WHERE "Id" BETWEEN 3001 AND 3020;
|
||||||
@@ -275,6 +287,8 @@ public partial class AdjustDemoSubcategoriesAndArchive : Migration
|
|||||||
SELECT setval(pg_get_serial_sequence('"ShowactApplications"', 'Id'), COALESCE((SELECT MAX("Id") FROM "ShowactApplications"), 1));
|
SELECT setval(pg_get_serial_sequence('"ShowactApplications"', 'Id'), COALESCE((SELECT MAX("Id") FROM "ShowactApplications"), 1));
|
||||||
SELECT setval(pg_get_serial_sequence('"Sponsors"', 'Id'), COALESCE((SELECT MAX("Id") FROM "Sponsors"), 1));
|
SELECT setval(pg_get_serial_sequence('"Sponsors"', 'Id'), COALESCE((SELECT MAX("Id") FROM "Sponsors"), 1));
|
||||||
SELECT setval(pg_get_serial_sequence('"RiskFlags"', 'Id'), COALESCE((SELECT MAX("Id") FROM "RiskFlags"), 1));
|
SELECT setval(pg_get_serial_sequence('"RiskFlags"', 'Id'), COALESCE((SELECT MAX("Id") FROM "RiskFlags"), 1));
|
||||||
|
END;
|
||||||
|
$vtsa_adjust$;
|
||||||
"""
|
"""
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,48 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddArchivedWinners : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ArchivedWinners",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
Year = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
Category = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
||||||
|
Subcategory = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
||||||
|
WinnerName = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
||||||
|
WinnerUrl = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: false),
|
||||||
|
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||||
|
UpdatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ArchivedWinners", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ArchivedWinners_Year_Category_Subcategory",
|
||||||
|
table: "ArchivedWinners",
|
||||||
|
columns: new[] { "Year", "Category", "Subcategory" },
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ArchivedWinners");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,306 @@
|
|||||||
|
using Backend.Data;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations;
|
||||||
|
|
||||||
|
[DbContext(typeof(AwardsDbContext))]
|
||||||
|
[Migration("20260629183000_SeedLiveDemoAwardData")]
|
||||||
|
public partial class SeedLiveDemoAwardData : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql(
|
||||||
|
"""
|
||||||
|
DO $vtsa_live_demo$
|
||||||
|
DECLARE
|
||||||
|
demo_year integer := 2030;
|
||||||
|
demo_season_id integer;
|
||||||
|
should_make_current boolean;
|
||||||
|
subcategory_templates text := '[{"name":"Hidden Star","slug":"hidden-star","sortOrder":1,"viewerRangeMin":1,"viewerRangeMax":20},{"name":"Rising Star","slug":"rising-star","sortOrder":2,"viewerRangeMin":21,"viewerRangeMax":60},{"name":"Shining Star","slug":"shining-star","sortOrder":3,"viewerRangeMin":61,"viewerRangeMax":null}]';
|
||||||
|
BEGIN
|
||||||
|
SELECT NOT EXISTS (SELECT 1 FROM "Seasons" WHERE "IsCurrent" = TRUE)
|
||||||
|
INTO should_make_current;
|
||||||
|
|
||||||
|
INSERT INTO "Seasons" (
|
||||||
|
"Year",
|
||||||
|
"Name",
|
||||||
|
"IsDemo",
|
||||||
|
"IsCurrent",
|
||||||
|
"IsCommunityOnly",
|
||||||
|
"CurrentPhase",
|
||||||
|
"NominationStartsAt",
|
||||||
|
"NominationEndsAt",
|
||||||
|
"VotingStartsAt",
|
||||||
|
"VotingEndsAt",
|
||||||
|
"ReviewStartsAt",
|
||||||
|
"ReviewEndsAt",
|
||||||
|
"ShowDate",
|
||||||
|
"ShowStartsAt",
|
||||||
|
"WinnersPublishedAt",
|
||||||
|
"WinnersPublishedByTwitchId",
|
||||||
|
"SubcategoryTemplatesJson",
|
||||||
|
"WorkflowRulesJson"
|
||||||
|
)
|
||||||
|
VALUES (
|
||||||
|
demo_year,
|
||||||
|
'VTuber Star Awards Demo Season',
|
||||||
|
TRUE,
|
||||||
|
should_make_current,
|
||||||
|
FALSE,
|
||||||
|
'Voting',
|
||||||
|
DATE '2030-05-01',
|
||||||
|
DATE '2030-05-31',
|
||||||
|
DATE '2030-06-01',
|
||||||
|
DATE '2030-06-30',
|
||||||
|
DATE '2030-07-01',
|
||||||
|
DATE '2030-07-10',
|
||||||
|
DATE '2030-07-20',
|
||||||
|
TIME '20:00',
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
subcategory_templates,
|
||||||
|
'[]'
|
||||||
|
)
|
||||||
|
ON CONFLICT ("Year") DO UPDATE
|
||||||
|
SET
|
||||||
|
"Name" = EXCLUDED."Name",
|
||||||
|
"IsDemo" = TRUE,
|
||||||
|
"IsCurrent" = CASE
|
||||||
|
WHEN should_make_current THEN TRUE
|
||||||
|
ELSE "Seasons"."IsCurrent"
|
||||||
|
END,
|
||||||
|
"IsCommunityOnly" = FALSE,
|
||||||
|
"CurrentPhase" = 'Voting',
|
||||||
|
"NominationStartsAt" = DATE '2030-05-01',
|
||||||
|
"NominationEndsAt" = DATE '2030-05-31',
|
||||||
|
"VotingStartsAt" = DATE '2030-06-01',
|
||||||
|
"VotingEndsAt" = DATE '2030-06-30',
|
||||||
|
"ReviewStartsAt" = DATE '2030-07-01',
|
||||||
|
"ReviewEndsAt" = DATE '2030-07-10',
|
||||||
|
"ShowDate" = DATE '2030-07-20',
|
||||||
|
"ShowStartsAt" = TIME '20:00',
|
||||||
|
"SubcategoryTemplatesJson" = subcategory_templates
|
||||||
|
RETURNING "Id" INTO demo_season_id;
|
||||||
|
|
||||||
|
IF should_make_current THEN
|
||||||
|
UPDATE "Seasons"
|
||||||
|
SET "IsCurrent" = FALSE
|
||||||
|
WHERE "Id" <> demo_season_id;
|
||||||
|
|
||||||
|
UPDATE "Seasons"
|
||||||
|
SET "IsCurrent" = TRUE
|
||||||
|
WHERE "Id" = demo_season_id;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
WITH award_groups(ord, group_name, group_slug, description, name_prefix) AS (
|
||||||
|
VALUES
|
||||||
|
(1, 'Creator des Jahres', 'creator-des-jahres', 'Allround-Creator mit starker Season, klarer Community-Praesenz und wiedererkennbarer Identitaet.', 'Astra'),
|
||||||
|
(2, 'Newcomer', 'newcomer', 'Neue Stimmen, Debuets und Creator, die in dieser Season besonders sichtbar geworden sind.', 'Nova'),
|
||||||
|
(3, 'Model und Design', 'model-design', 'Live2D, 3D, Outfit, Rigging und visuelle Praesentation im Stream.', 'Velvet'),
|
||||||
|
(4, 'Gesang und Musik', 'gesang-musik', 'Covers, Originale, Live-Gesang, Musikproduktion und Buehnenmomente.', 'Melo'),
|
||||||
|
(5, 'Gaming', 'gaming', 'Gameplay, Skill, Chaos, Speedruns und starke Gaming-Unterhaltung.', 'Pixel'),
|
||||||
|
(6, 'Variety und Entertainment', 'variety-entertainment', 'Talk, Comedy, Watchalongs, Challenges und kreative Streamformate.', 'Taro'),
|
||||||
|
(7, 'Community', 'community', 'Creator, die ihre Community sichtbar verbinden, einladen und aktiv einbeziehen.', 'Lumi'),
|
||||||
|
(8, 'Collab und Duo', 'collab-duo', 'Gemeinsame Streams, Duo-Dynamik und Projekte, die zusammen staerker wirken.', 'Orbit')
|
||||||
|
),
|
||||||
|
tiers(ord, tier_name, tier_slug, min_viewers, max_viewers) AS (
|
||||||
|
VALUES
|
||||||
|
(1, 'Hidden Star', 'hidden-star', 1, 20),
|
||||||
|
(2, 'Rising Star', 'rising-star', 21, 60),
|
||||||
|
(3, 'Shining Star', 'shining-star', 61, NULL)
|
||||||
|
)
|
||||||
|
INSERT INTO "Categories" (
|
||||||
|
"SeasonId",
|
||||||
|
"GroupName",
|
||||||
|
"Name",
|
||||||
|
"Slug",
|
||||||
|
"Description",
|
||||||
|
"SortOrder",
|
||||||
|
"MaxNomineesPerUser",
|
||||||
|
"ViewerRangeMin",
|
||||||
|
"ViewerRangeMax"
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
demo_season_id,
|
||||||
|
award_groups.group_name,
|
||||||
|
tiers.tier_name,
|
||||||
|
award_groups.group_slug || '-' || tiers.tier_slug,
|
||||||
|
award_groups.description,
|
||||||
|
(award_groups.ord * 100) + (tiers.ord * 10),
|
||||||
|
3,
|
||||||
|
tiers.min_viewers,
|
||||||
|
tiers.max_viewers
|
||||||
|
FROM award_groups
|
||||||
|
CROSS JOIN tiers
|
||||||
|
ON CONFLICT ("SeasonId", "Slug") DO UPDATE
|
||||||
|
SET
|
||||||
|
"GroupName" = EXCLUDED."GroupName",
|
||||||
|
"Name" = EXCLUDED."Name",
|
||||||
|
"Description" = EXCLUDED."Description",
|
||||||
|
"SortOrder" = EXCLUDED."SortOrder",
|
||||||
|
"MaxNomineesPerUser" = EXCLUDED."MaxNomineesPerUser",
|
||||||
|
"ViewerRangeMin" = EXCLUDED."ViewerRangeMin",
|
||||||
|
"ViewerRangeMax" = EXCLUDED."ViewerRangeMax";
|
||||||
|
|
||||||
|
WITH award_groups(ord, group_name, group_slug, name_prefix) AS (
|
||||||
|
VALUES
|
||||||
|
(1, 'Creator des Jahres', 'creator-des-jahres', 'Astra'),
|
||||||
|
(2, 'Newcomer', 'newcomer', 'Nova'),
|
||||||
|
(3, 'Model und Design', 'model-design', 'Velvet'),
|
||||||
|
(4, 'Gesang und Musik', 'gesang-musik', 'Melo'),
|
||||||
|
(5, 'Gaming', 'gaming', 'Pixel'),
|
||||||
|
(6, 'Variety und Entertainment', 'variety-entertainment', 'Taro'),
|
||||||
|
(7, 'Community', 'community', 'Lumi'),
|
||||||
|
(8, 'Collab und Duo', 'collab-duo', 'Orbit')
|
||||||
|
),
|
||||||
|
tiers(ord, tier_name, tier_slug) AS (
|
||||||
|
VALUES
|
||||||
|
(1, 'Hidden Star', 'hidden-star'),
|
||||||
|
(2, 'Rising Star', 'rising-star'),
|
||||||
|
(3, 'Shining Star', 'shining-star')
|
||||||
|
),
|
||||||
|
slots(ord, name_suffix, platform) AS (
|
||||||
|
VALUES
|
||||||
|
(1, 'Aster', 'Twitch'),
|
||||||
|
(2, 'Vega', 'YouTube')
|
||||||
|
),
|
||||||
|
candidate_rows AS (
|
||||||
|
SELECT
|
||||||
|
categories."Id" AS category_id,
|
||||||
|
award_groups.ord AS group_ord,
|
||||||
|
tiers.ord AS tier_ord,
|
||||||
|
slots.ord AS slot_ord,
|
||||||
|
award_groups.name_prefix || ' ' || slots.name_suffix || ' (' || tiers.tier_name || ')' AS display_name,
|
||||||
|
regexp_replace(
|
||||||
|
lower('vtsa_demo_' || award_groups.name_prefix || '_' || tiers.tier_slug || '_' || slots.name_suffix),
|
||||||
|
'[^a-z0-9]+',
|
||||||
|
'_',
|
||||||
|
'g'
|
||||||
|
) AS channel_slug,
|
||||||
|
slots.platform
|
||||||
|
FROM award_groups
|
||||||
|
CROSS JOIN tiers
|
||||||
|
CROSS JOIN slots
|
||||||
|
INNER JOIN "Categories" categories
|
||||||
|
ON categories."SeasonId" = demo_season_id
|
||||||
|
AND categories."Slug" = award_groups.group_slug || '-' || tiers.tier_slug
|
||||||
|
)
|
||||||
|
INSERT INTO "Candidates" (
|
||||||
|
"SeasonId",
|
||||||
|
"CategoryId",
|
||||||
|
"StreamerIdentityId",
|
||||||
|
"DisplayName",
|
||||||
|
"ChannelSlug",
|
||||||
|
"Platform",
|
||||||
|
"NominationTally",
|
||||||
|
"AcceptanceStatus",
|
||||||
|
"AcceptanceNote",
|
||||||
|
"ClipCompilationUrl",
|
||||||
|
"ClipCompilationTitle",
|
||||||
|
"ClipCompilationPlatform",
|
||||||
|
"ClipEmbedStatus"
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
demo_season_id,
|
||||||
|
candidate_rows.category_id,
|
||||||
|
NULL,
|
||||||
|
candidate_rows.display_name,
|
||||||
|
candidate_rows.channel_slug,
|
||||||
|
candidate_rows.platform,
|
||||||
|
4 + (candidate_rows.group_ord * 2) + candidate_rows.tier_ord + candidate_rows.slot_ord,
|
||||||
|
'approved',
|
||||||
|
'Demo-Kandidat fuer Live-Testdaten.',
|
||||||
|
CASE
|
||||||
|
WHEN candidate_rows.slot_ord = 1 THEN 'https://youtu.be/demo-' || candidate_rows.channel_slug
|
||||||
|
ELSE NULL
|
||||||
|
END,
|
||||||
|
CASE
|
||||||
|
WHEN candidate_rows.slot_ord = 1 THEN candidate_rows.display_name || ' Highlight Reel'
|
||||||
|
ELSE NULL
|
||||||
|
END,
|
||||||
|
CASE
|
||||||
|
WHEN candidate_rows.slot_ord = 1 THEN 'YouTube'
|
||||||
|
ELSE NULL
|
||||||
|
END,
|
||||||
|
CASE
|
||||||
|
WHEN candidate_rows.slot_ord = 1 THEN 'available'
|
||||||
|
ELSE 'unchecked'
|
||||||
|
END
|
||||||
|
FROM candidate_rows
|
||||||
|
WHERE NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM "Candidates" existing
|
||||||
|
WHERE existing."SeasonId" = demo_season_id
|
||||||
|
AND lower(existing."ChannelSlug") = lower(candidate_rows.channel_slug)
|
||||||
|
AND existing."CategoryId" = candidate_rows.category_id
|
||||||
|
);
|
||||||
|
|
||||||
|
PERFORM setval(pg_get_serial_sequence('"Seasons"', 'Id'), COALESCE((SELECT MAX("Id") FROM "Seasons"), 1));
|
||||||
|
PERFORM setval(pg_get_serial_sequence('"Categories"', 'Id'), COALESCE((SELECT MAX("Id") FROM "Categories"), 1));
|
||||||
|
PERFORM setval(pg_get_serial_sequence('"Candidates"', 'Id'), COALESCE((SELECT MAX("Id") FROM "Candidates"), 1));
|
||||||
|
END;
|
||||||
|
$vtsa_live_demo$;
|
||||||
|
"""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql(
|
||||||
|
"""
|
||||||
|
DO $vtsa_live_demo_down$
|
||||||
|
DECLARE
|
||||||
|
demo_season_id integer;
|
||||||
|
BEGIN
|
||||||
|
SELECT "Id"
|
||||||
|
INTO demo_season_id
|
||||||
|
FROM "Seasons"
|
||||||
|
WHERE "Year" = 2030
|
||||||
|
AND "IsDemo" = TRUE
|
||||||
|
AND "Name" = 'VTuber Star Awards Demo Season';
|
||||||
|
|
||||||
|
IF demo_season_id IS NULL THEN
|
||||||
|
RETURN;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
DELETE FROM "Candidates"
|
||||||
|
WHERE "SeasonId" = demo_season_id
|
||||||
|
AND "ChannelSlug" LIKE 'vtsa_demo_%';
|
||||||
|
|
||||||
|
DELETE FROM "Categories"
|
||||||
|
WHERE "SeasonId" = demo_season_id
|
||||||
|
AND "Slug" IN (
|
||||||
|
'creator-des-jahres-hidden-star',
|
||||||
|
'creator-des-jahres-rising-star',
|
||||||
|
'creator-des-jahres-shining-star',
|
||||||
|
'newcomer-hidden-star',
|
||||||
|
'newcomer-rising-star',
|
||||||
|
'newcomer-shining-star',
|
||||||
|
'model-design-hidden-star',
|
||||||
|
'model-design-rising-star',
|
||||||
|
'model-design-shining-star',
|
||||||
|
'gesang-musik-hidden-star',
|
||||||
|
'gesang-musik-rising-star',
|
||||||
|
'gesang-musik-shining-star',
|
||||||
|
'gaming-hidden-star',
|
||||||
|
'gaming-rising-star',
|
||||||
|
'gaming-shining-star',
|
||||||
|
'variety-entertainment-hidden-star',
|
||||||
|
'variety-entertainment-rising-star',
|
||||||
|
'variety-entertainment-shining-star',
|
||||||
|
'community-hidden-star',
|
||||||
|
'community-rising-star',
|
||||||
|
'community-shining-star',
|
||||||
|
'collab-duo-hidden-star',
|
||||||
|
'collab-duo-rising-star',
|
||||||
|
'collab-duo-shining-star'
|
||||||
|
);
|
||||||
|
END;
|
||||||
|
$vtsa_live_demo_down$;
|
||||||
|
"""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,206 @@
|
|||||||
|
using Backend.Data;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations;
|
||||||
|
|
||||||
|
[DbContext(typeof(AwardsDbContext))]
|
||||||
|
[Migration("20260629185000_SeedCurrentSeasonDemoVotingData")]
|
||||||
|
public partial class SeedCurrentSeasonDemoVotingData : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql(
|
||||||
|
"""
|
||||||
|
DO $vtsa_current_demo$
|
||||||
|
DECLARE
|
||||||
|
target_season_id integer;
|
||||||
|
subcategory_templates text := '[{"name":"Hidden Star","slug":"hidden-star","sortOrder":1,"viewerRangeMin":1,"viewerRangeMax":20},{"name":"Rising Star","slug":"rising-star","sortOrder":2,"viewerRangeMin":21,"viewerRangeMax":60},{"name":"Shining Star","slug":"shining-star","sortOrder":3,"viewerRangeMin":61,"viewerRangeMax":null}]';
|
||||||
|
BEGIN
|
||||||
|
SELECT "Id"
|
||||||
|
INTO target_season_id
|
||||||
|
FROM "Seasons"
|
||||||
|
WHERE "IsCurrent" = TRUE
|
||||||
|
ORDER BY "Year" DESC
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
IF target_season_id IS NULL THEN
|
||||||
|
SELECT "Id"
|
||||||
|
INTO target_season_id
|
||||||
|
FROM "Seasons"
|
||||||
|
WHERE "Year" = 2030
|
||||||
|
ORDER BY "Id" DESC
|
||||||
|
LIMIT 1;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF target_season_id IS NULL THEN
|
||||||
|
RETURN;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
UPDATE "Seasons"
|
||||||
|
SET "SubcategoryTemplatesJson" = subcategory_templates
|
||||||
|
WHERE "Id" = target_season_id;
|
||||||
|
|
||||||
|
WITH award_groups(ord, group_name, group_slug, description, name_prefix) AS (
|
||||||
|
VALUES
|
||||||
|
(1, 'Main Awards', 'main-awards', 'Die groessten Allround-Auszeichnungen der aktuellen Season.', 'Astra'),
|
||||||
|
(2, 'Discovery', 'discovery', 'Neue Stimmen, Debuets und Creator, die in dieser Season besonders sichtbar geworden sind.', 'Nova'),
|
||||||
|
(3, 'Creative', 'creative', 'Model, Design, Rigging, Art und visuelle Praesentation im Stream.', 'Velvet'),
|
||||||
|
(4, 'Performance', 'performance', 'Gesang, Musik, Buehnenpraesenz und besondere Live-Momente.', 'Melo'),
|
||||||
|
(5, 'Gaming', 'gaming', 'Gameplay, Skill, Chaos, Speedruns und starke Gaming-Unterhaltung.', 'Pixel'),
|
||||||
|
(6, 'Entertainment', 'entertainment', 'Talk, Comedy, Watchalongs, Challenges und kreative Streamformate.', 'Taro'),
|
||||||
|
(7, 'Community', 'community', 'Creator, die ihre Community sichtbar verbinden, einladen und aktiv einbeziehen.', 'Lumi'),
|
||||||
|
(8, 'Collab', 'collab', 'Gemeinsame Streams, Duo-Dynamik und Projekte, die zusammen staerker wirken.', 'Orbit')
|
||||||
|
),
|
||||||
|
tiers(ord, tier_name, tier_slug, min_viewers, max_viewers) AS (
|
||||||
|
VALUES
|
||||||
|
(1, 'Hidden Star', 'hidden-star', 1, 20),
|
||||||
|
(2, 'Rising Star', 'rising-star', 21, 60),
|
||||||
|
(3, 'Shining Star', 'shining-star', 61, NULL)
|
||||||
|
)
|
||||||
|
INSERT INTO "Categories" (
|
||||||
|
"SeasonId",
|
||||||
|
"GroupName",
|
||||||
|
"Name",
|
||||||
|
"Slug",
|
||||||
|
"Description",
|
||||||
|
"SortOrder",
|
||||||
|
"MaxNomineesPerUser",
|
||||||
|
"ViewerRangeMin",
|
||||||
|
"ViewerRangeMax"
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
target_season_id,
|
||||||
|
award_groups.group_name,
|
||||||
|
tiers.tier_name,
|
||||||
|
award_groups.group_slug || '-' || tiers.tier_slug,
|
||||||
|
award_groups.description,
|
||||||
|
(award_groups.ord * 100) + (tiers.ord * 10),
|
||||||
|
3,
|
||||||
|
tiers.min_viewers,
|
||||||
|
tiers.max_viewers
|
||||||
|
FROM award_groups
|
||||||
|
CROSS JOIN tiers
|
||||||
|
ON CONFLICT ("SeasonId", "Slug") DO UPDATE
|
||||||
|
SET
|
||||||
|
"GroupName" = EXCLUDED."GroupName",
|
||||||
|
"Name" = EXCLUDED."Name",
|
||||||
|
"Description" = EXCLUDED."Description",
|
||||||
|
"SortOrder" = EXCLUDED."SortOrder",
|
||||||
|
"MaxNomineesPerUser" = EXCLUDED."MaxNomineesPerUser",
|
||||||
|
"ViewerRangeMin" = EXCLUDED."ViewerRangeMin",
|
||||||
|
"ViewerRangeMax" = EXCLUDED."ViewerRangeMax";
|
||||||
|
|
||||||
|
WITH award_groups(ord, group_name, group_slug, name_prefix) AS (
|
||||||
|
VALUES
|
||||||
|
(1, 'Main Awards', 'main-awards', 'Astra'),
|
||||||
|
(2, 'Discovery', 'discovery', 'Nova'),
|
||||||
|
(3, 'Creative', 'creative', 'Velvet'),
|
||||||
|
(4, 'Performance', 'performance', 'Melo'),
|
||||||
|
(5, 'Gaming', 'gaming', 'Pixel'),
|
||||||
|
(6, 'Entertainment', 'entertainment', 'Taro'),
|
||||||
|
(7, 'Community', 'community', 'Lumi'),
|
||||||
|
(8, 'Collab', 'collab', 'Orbit')
|
||||||
|
),
|
||||||
|
tiers(ord, tier_name, tier_slug) AS (
|
||||||
|
VALUES
|
||||||
|
(1, 'Hidden Star', 'hidden-star'),
|
||||||
|
(2, 'Rising Star', 'rising-star'),
|
||||||
|
(3, 'Shining Star', 'shining-star')
|
||||||
|
),
|
||||||
|
slots(ord, name_suffix, platform) AS (
|
||||||
|
VALUES
|
||||||
|
(1, 'Aster', 'Twitch'),
|
||||||
|
(2, 'Vega', 'YouTube')
|
||||||
|
),
|
||||||
|
candidate_rows AS (
|
||||||
|
SELECT
|
||||||
|
categories."Id" AS category_id,
|
||||||
|
award_groups.ord AS group_ord,
|
||||||
|
tiers.ord AS tier_ord,
|
||||||
|
slots.ord AS slot_ord,
|
||||||
|
award_groups.name_prefix || ' ' || slots.name_suffix || ' (' || tiers.tier_name || ')' AS display_name,
|
||||||
|
regexp_replace(
|
||||||
|
lower('vtsa_demo_current_' || award_groups.name_prefix || '_' || tiers.tier_slug || '_' || slots.name_suffix),
|
||||||
|
'[^a-z0-9]+',
|
||||||
|
'_',
|
||||||
|
'g'
|
||||||
|
) AS channel_slug,
|
||||||
|
slots.platform
|
||||||
|
FROM award_groups
|
||||||
|
CROSS JOIN tiers
|
||||||
|
CROSS JOIN slots
|
||||||
|
INNER JOIN "Categories" categories
|
||||||
|
ON categories."SeasonId" = target_season_id
|
||||||
|
AND categories."Slug" = award_groups.group_slug || '-' || tiers.tier_slug
|
||||||
|
)
|
||||||
|
INSERT INTO "Candidates" (
|
||||||
|
"SeasonId",
|
||||||
|
"CategoryId",
|
||||||
|
"StreamerIdentityId",
|
||||||
|
"DisplayName",
|
||||||
|
"ChannelSlug",
|
||||||
|
"Platform",
|
||||||
|
"NominationTally",
|
||||||
|
"AcceptanceStatus",
|
||||||
|
"AcceptanceNote",
|
||||||
|
"ClipCompilationUrl",
|
||||||
|
"ClipCompilationTitle",
|
||||||
|
"ClipCompilationPlatform",
|
||||||
|
"ClipEmbedStatus"
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
target_season_id,
|
||||||
|
candidate_rows.category_id,
|
||||||
|
NULL,
|
||||||
|
candidate_rows.display_name,
|
||||||
|
candidate_rows.channel_slug,
|
||||||
|
candidate_rows.platform,
|
||||||
|
4 + (candidate_rows.group_ord * 2) + candidate_rows.tier_ord + candidate_rows.slot_ord,
|
||||||
|
'approved',
|
||||||
|
'Demo-Kandidat fuer den aktuellen Live-Voting-Test.',
|
||||||
|
CASE
|
||||||
|
WHEN candidate_rows.slot_ord = 1 THEN 'https://youtu.be/demo-' || candidate_rows.channel_slug
|
||||||
|
ELSE NULL
|
||||||
|
END,
|
||||||
|
CASE
|
||||||
|
WHEN candidate_rows.slot_ord = 1 THEN candidate_rows.display_name || ' Highlight Reel'
|
||||||
|
ELSE NULL
|
||||||
|
END,
|
||||||
|
CASE
|
||||||
|
WHEN candidate_rows.slot_ord = 1 THEN 'YouTube'
|
||||||
|
ELSE NULL
|
||||||
|
END,
|
||||||
|
CASE
|
||||||
|
WHEN candidate_rows.slot_ord = 1 THEN 'available'
|
||||||
|
ELSE 'unchecked'
|
||||||
|
END
|
||||||
|
FROM candidate_rows
|
||||||
|
WHERE NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM "Candidates" existing
|
||||||
|
WHERE existing."SeasonId" = target_season_id
|
||||||
|
AND lower(existing."ChannelSlug") = lower(candidate_rows.channel_slug)
|
||||||
|
AND existing."CategoryId" = candidate_rows.category_id
|
||||||
|
);
|
||||||
|
|
||||||
|
PERFORM setval(pg_get_serial_sequence('"Categories"', 'Id'), COALESCE((SELECT MAX("Id") FROM "Categories"), 1));
|
||||||
|
PERFORM setval(pg_get_serial_sequence('"Candidates"', 'Id'), COALESCE((SELECT MAX("Id") FROM "Candidates"), 1));
|
||||||
|
END;
|
||||||
|
$vtsa_current_demo$;
|
||||||
|
"""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql(
|
||||||
|
"""
|
||||||
|
DELETE FROM "Candidates"
|
||||||
|
WHERE "ChannelSlug" LIKE 'vtsa_demo_current_%';
|
||||||
|
"""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
+1620
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,72 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class SeedDemoSponsorsAndShareLinks : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql(
|
||||||
|
"""
|
||||||
|
UPDATE "SiteSettings"
|
||||||
|
SET
|
||||||
|
"ShareXUrl" = CASE
|
||||||
|
WHEN COALESCE(NULLIF("ShareXUrl", ''), '') IN ('', 'https://x.com/intent/tweet')
|
||||||
|
THEN 'https://x.com/intent/tweet?text=Schaut%20euch%20die%20VTuber%20Star%20Awards%20an!&url=https%3A%2F%2Faward.noveria.net'
|
||||||
|
ELSE "ShareXUrl"
|
||||||
|
END,
|
||||||
|
"ShareDiscordUrl" = CASE
|
||||||
|
WHEN COALESCE(NULLIF("ShareDiscordUrl", ''), '') IN ('', 'https://discord.gg/')
|
||||||
|
THEN 'https://discord.gg/jayuhime'
|
||||||
|
ELSE "ShareDiscordUrl"
|
||||||
|
END
|
||||||
|
WHERE "Id" = 1;
|
||||||
|
|
||||||
|
INSERT INTO "Sponsors" (
|
||||||
|
"Id", "SeasonId", "Name", "WebsiteUrl", "LogoUrl", "Description", "Tier", "SortOrder", "IsVisible", "CreatedAt", "UpdatedAt"
|
||||||
|
)
|
||||||
|
SELECT seed."Id", seed."SeasonId", seed."Name", seed."WebsiteUrl", seed."LogoUrl", seed."Description", seed."Tier", seed."SortOrder", seed."IsVisible", seed."CreatedAt", seed."UpdatedAt"
|
||||||
|
FROM (
|
||||||
|
VALUES
|
||||||
|
(9006, 1001, 'Starfall Merch Lab', 'https://example.invalid/starfall-merch', '/demo/sponsors/chibicanvas-market.svg', 'Merch-Pakete, Sticker-Bundles und kleine Giveaways fuer Community-Aktionen rund um die Show.', 'Merch Partner', 60, TRUE, TIMESTAMPTZ '2026-06-01 10:25:00+00', NULL::timestamptz),
|
||||||
|
(9007, 1001, 'Moonframe Media', 'https://example.invalid/moonframe-media', '/demo/sponsors/prismloop-audio.svg', 'Highlight-Cuts, Social-Assets und kurze Promo-Snippets fuer Voting- und Finale-Phasen.', 'Media Partner', 70, TRUE, TIMESTAMPTZ '2026-06-01 10:30:00+00', NULL::timestamptz),
|
||||||
|
(9008, 1001, 'PixelHarbor Tools', 'https://example.invalid/pixelharbor-tools', '/demo/sponsors/cloudbeacon-hosting.svg', 'Kleine Creator-Tools fuer Landingpages, Formulare und Community-Orga im Eventbetrieb.', 'Tooling Partner', 80, TRUE, TIMESTAMPTZ '2026-06-01 10:35:00+00', NULL::timestamptz)
|
||||||
|
) AS seed("Id", "SeasonId", "Name", "WebsiteUrl", "LogoUrl", "Description", "Tier", "SortOrder", "IsVisible", "CreatedAt", "UpdatedAt")
|
||||||
|
WHERE EXISTS (SELECT 1 FROM "Seasons" WHERE "Id" = seed."SeasonId")
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM "Sponsors" existing
|
||||||
|
WHERE existing."SeasonId" = seed."SeasonId"
|
||||||
|
AND existing."Name" = seed."Name"
|
||||||
|
);
|
||||||
|
|
||||||
|
SELECT setval(pg_get_serial_sequence('"Sponsors"', 'Id'), COALESCE((SELECT MAX("Id") FROM "Sponsors"), 1));
|
||||||
|
"""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql(
|
||||||
|
"""
|
||||||
|
DELETE FROM "Sponsors"
|
||||||
|
WHERE "Id" IN (9006, 9007, 9008)
|
||||||
|
AND "SeasonId" = 1001;
|
||||||
|
|
||||||
|
UPDATE "SiteSettings"
|
||||||
|
SET
|
||||||
|
"ShareXUrl" = 'https://x.com/intent/tweet',
|
||||||
|
"ShareDiscordUrl" = 'https://discord.gg/'
|
||||||
|
WHERE "Id" = 1
|
||||||
|
AND "ShareXUrl" = 'https://x.com/intent/tweet?text=Schaut%20euch%20die%20VTuber%20Star%20Awards%20an!&url=https%3A%2F%2Faward.noveria.net'
|
||||||
|
AND "ShareDiscordUrl" = 'https://discord.gg/jayuhime';
|
||||||
|
"""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1620
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,71 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class SeedExampleSponsorsForCurrentSeason : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql(
|
||||||
|
"""
|
||||||
|
WITH target_season AS (
|
||||||
|
SELECT "Id"
|
||||||
|
FROM "Seasons"
|
||||||
|
WHERE "IsCurrent" = TRUE
|
||||||
|
ORDER BY "Year" DESC, "Id" DESC
|
||||||
|
LIMIT 1
|
||||||
|
)
|
||||||
|
INSERT INTO "Sponsors" (
|
||||||
|
"SeasonId", "Name", "WebsiteUrl", "LogoUrl", "Description", "Tier", "SortOrder", "IsVisible", "CreatedAt", "UpdatedAt"
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
target_season."Id",
|
||||||
|
seed."Name",
|
||||||
|
seed."WebsiteUrl",
|
||||||
|
seed."LogoUrl",
|
||||||
|
seed."Description",
|
||||||
|
seed."Tier",
|
||||||
|
seed."SortOrder",
|
||||||
|
TRUE,
|
||||||
|
TIMESTAMPTZ '2026-06-29 19:22:20+00',
|
||||||
|
NULL::timestamptz
|
||||||
|
FROM target_season
|
||||||
|
CROSS JOIN (
|
||||||
|
VALUES
|
||||||
|
('HoshiForge Studio', 'https://example.invalid/hoshiforge', '/demo/sponsors/hoshiforge-studio.svg', 'Branding-, Overlay- und Debuet-Visuals fuer VTuber-Projekte und Community-Events.', 'Presenting Sponsor', 10),
|
||||||
|
('NekoPixel Energy', 'https://example.invalid/nekopixel', '/demo/sponsors/nekopixel-energy.svg', 'Community-fokussierter Drink-Partner fuer lange Showabende, Watchpartys und Creator-Collabs.', 'Gold Partner', 20),
|
||||||
|
('PrismLoop Audio', 'https://example.invalid/prismloop', '/demo/sponsors/prismloop-audio.svg', 'Audio-Tools, Intro-Packs und Stream-Sounddesign fuer Live-Shows und Highlight-Clips.', 'Gold Partner', 30),
|
||||||
|
('CloudBeacon Hosting', 'https://example.invalid/cloudbeacon', '/demo/sponsors/cloudbeacon-hosting.svg', 'Skalierbares Hosting fuer Voting, Landingpages und Event-Traffic rund um Showtage.', 'Tech Partner', 40),
|
||||||
|
('ChibiCanvas Market', 'https://example.invalid/chibicanvas', '/demo/sponsors/chibicanvas-market.svg', 'Merch-, Sticker- und Artist-Marketplace mit Fokus auf VTuber, Emotes und Fanartikel.', 'Community Partner', 50)
|
||||||
|
) AS seed("Name", "WebsiteUrl", "LogoUrl", "Description", "Tier", "SortOrder")
|
||||||
|
WHERE NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM "Sponsors" existing
|
||||||
|
WHERE existing."SeasonId" = target_season."Id"
|
||||||
|
);
|
||||||
|
"""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql(
|
||||||
|
"""
|
||||||
|
DELETE FROM "Sponsors"
|
||||||
|
WHERE "Name" IN (
|
||||||
|
'HoshiForge Studio',
|
||||||
|
'NekoPixel Energy',
|
||||||
|
'PrismLoop Audio',
|
||||||
|
'CloudBeacon Hosting',
|
||||||
|
'ChibiCanvas Market'
|
||||||
|
);
|
||||||
|
"""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations;
|
||||||
|
|
||||||
|
[DbContext(typeof(Data.AwardsDbContext))]
|
||||||
|
[Migration("20260629194000_RemoveDemoSeasons")]
|
||||||
|
public partial class RemoveDemoSeasons : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql(
|
||||||
|
"""
|
||||||
|
UPDATE "RiskFlags"
|
||||||
|
SET "SeasonId" = NULL
|
||||||
|
WHERE "SeasonId" IN (
|
||||||
|
SELECT "Id"
|
||||||
|
FROM "Seasons"
|
||||||
|
WHERE "IsDemo" = TRUE
|
||||||
|
);
|
||||||
|
|
||||||
|
DELETE FROM "Seasons"
|
||||||
|
WHERE "IsDemo" = TRUE;
|
||||||
|
"""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
using Backend.Data;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(AwardsDbContext))]
|
||||||
|
[Migration("20260629203000_AddHostImageUpload")]
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddHostImageUpload : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql(
|
||||||
|
"""
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "HostImageData" bytea;
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "HostImageContentType" character varying(80);
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "HostImageUpdatedAt" timestamp with time zone;
|
||||||
|
"""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql(
|
||||||
|
"""
|
||||||
|
ALTER TABLE "SiteSettings" DROP COLUMN IF EXISTS "HostImageUpdatedAt";
|
||||||
|
ALTER TABLE "SiteSettings" DROP COLUMN IF EXISTS "HostImageContentType";
|
||||||
|
ALTER TABLE "SiteSettings" DROP COLUMN IF EXISTS "HostImageData";
|
||||||
|
"""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
using Backend.Data;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(AwardsDbContext))]
|
||||||
|
[Migration("20260629204500_AddHostArtistName")]
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddHostArtistName : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql(
|
||||||
|
"""
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "HostArtistName" character varying(120) NOT NULL DEFAULT '';
|
||||||
|
"""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql(
|
||||||
|
"""
|
||||||
|
ALTER TABLE "SiteSettings" DROP COLUMN IF EXISTS "HostArtistName";
|
||||||
|
"""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
using Backend.Data;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Backend.Migrations;
|
||||||
|
|
||||||
|
[DbContext(typeof(AwardsDbContext))]
|
||||||
|
[Migration("20260629210000_EnsureRuntimeSchemaParity")]
|
||||||
|
public partial class EnsureRuntimeSchemaParity : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql(
|
||||||
|
"""
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "HostImageData" bytea;
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "HostImageContentType" character varying(80);
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "HostImageUpdatedAt" timestamp with time zone;
|
||||||
|
ALTER TABLE "SiteSettings" ADD COLUMN IF NOT EXISTS "HostArtistName" character varying(120) NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE "Nominations" ADD COLUMN IF NOT EXISTS "StreamUrl" character varying(300);
|
||||||
|
"""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql(
|
||||||
|
"""
|
||||||
|
ALTER TABLE "Nominations" DROP COLUMN IF EXISTS "StreamUrl";
|
||||||
|
ALTER TABLE "SiteSettings" DROP COLUMN IF EXISTS "HostArtistName";
|
||||||
|
ALTER TABLE "SiteSettings" DROP COLUMN IF EXISTS "HostImageUpdatedAt";
|
||||||
|
ALTER TABLE "SiteSettings" DROP COLUMN IF EXISTS "HostImageContentType";
|
||||||
|
ALTER TABLE "SiteSettings" DROP COLUMN IF EXISTS "HostImageData";
|
||||||
|
"""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user