diff --git a/LANCommander.Server/Controllers/Api/KeysController.cs b/LANCommander.Server/Controllers/Api/KeysController.cs index b291ba17..97660682 100644 --- a/LANCommander.Server/Controllers/Api/KeysController.cs +++ b/LANCommander.Server/Controllers/Api/KeysController.cs @@ -6,6 +6,8 @@ using LANCommander.Server.Services; using LANCommander.SDK.Models; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Identity; +using LANCommander.Server.Models; namespace LANCommander.Server.Controllers.Api { @@ -16,14 +18,20 @@ namespace LANCommander.Server.Controllers.Api { private readonly IMapper Mapper; private readonly KeyService KeyService; + private readonly GameService GameService; + private readonly UserManager UserManager; public KeysController( ILogger logger, IMapper mapper, - KeyService keyService) : base(logger) + KeyService keyService, + GameService gameService, + UserManager userManager) : base(logger) { Mapper = mapper; KeyService = keyService; + GameService = gameService; + UserManager = userManager; } [HttpPost] @@ -32,43 +40,102 @@ namespace LANCommander.Server.Controllers.Api return Mapper.Map(KeyService.Get(k => k.AllocationMethod == Data.Models.KeyAllocationMethod.MacAddress && k.ClaimedByMacAddress == keyRequest.MacAddress).First()); } + /// + /// Get allocated key (or allocate new key) based on game's key allocation method + /// + /// ID of the game + /// + /// Allocated key [HttpPost("GetAllocated/{id}")] public async Task GetAllocated(Guid id, KeyRequest keyRequest) { - var existing = KeyService.Get(k => k.Game.Id == id && k.AllocationMethod == Data.Models.KeyAllocationMethod.MacAddress && k.ClaimedByMacAddress == keyRequest.MacAddress).FirstOrDefault(); + Data.Models.Key key = null; - if (existing != null) - return Mapper.Map(existing); + var user = await UserManager.FindByNameAsync(User.Identity.Name); + var game = await GameService.Get(id); + + switch (game.KeyAllocationMethod) + { + case Data.Models.KeyAllocationMethod.MacAddress: + key = game.Keys.FirstOrDefault(k => k.AllocationMethod == Data.Models.KeyAllocationMethod.MacAddress && k.ClaimedByMacAddress == keyRequest.MacAddress); + break; + + case Data.Models.KeyAllocationMethod.UserAccount: + key = game.Keys.FirstOrDefault(k => k.AllocationMethod == Data.Models.KeyAllocationMethod.UserAccount && k.ClaimedByUser?.Id == user.Id); + break; + } + + if (key != null) + return Mapper.Map(key); else - return Mapper.Map(await AllocateNewKey(id, keyRequest)); + return Mapper.Map(await AllocateNewKey(id, keyRequest, game.KeyAllocationMethod)); } + /// + /// Allocate a new key based on game's key allocation method + /// + /// ID of the game + /// + /// Newly allocated key [HttpPost("Allocate/{id}")] public async Task Allocate(Guid id, KeyRequest keyRequest) { - var existing = KeyService.Get(k => k.Game.Id == id && k.AllocationMethod == Data.Models.KeyAllocationMethod.MacAddress && keyRequest.MacAddress == keyRequest.MacAddress).FirstOrDefault(); + Data.Models.Key key = null; - var availableKey = KeyService.Get(k => k.Game.Id == id) - .Where(k => + var user = await UserManager.FindByNameAsync(User.Identity.Name); + var game = await GameService.Get(id); + + switch (game.KeyAllocationMethod) + { + case Data.Models.KeyAllocationMethod.MacAddress: + key = game.Keys.FirstOrDefault(k => k.AllocationMethod == Data.Models.KeyAllocationMethod.MacAddress && k.ClaimedByMacAddress == keyRequest.MacAddress); + break; + + case Data.Models.KeyAllocationMethod.UserAccount: + key = game.Keys.FirstOrDefault(k => k.AllocationMethod == Data.Models.KeyAllocationMethod.UserAccount && k.ClaimedByUser?.Id == user.Id); + break; + } + + var availableKey = game.Keys.FirstOrDefault(k => (k.AllocationMethod == Data.Models.KeyAllocationMethod.MacAddress && String.IsNullOrWhiteSpace(k.ClaimedByMacAddress)) || - (k.AllocationMethod == Data.Models.KeyAllocationMethod.UserAccount && k.ClaimedByUser == null)) - .FirstOrDefault(); + (k.AllocationMethod == Data.Models.KeyAllocationMethod.UserAccount && k.ClaimedByUser == null)); - if (availableKey == null && existing != null) - return Mapper.Map(existing); + if (availableKey == null && key != null) + return Mapper.Map(key); else if (availableKey == null) return null; else { - await KeyService.Release(existing.Id); + if (key != null) + await KeyService.Release(key.Id); - return Mapper.Map(await KeyService.Allocate(availableKey, keyRequest.MacAddress)); + switch (game.KeyAllocationMethod) + { + case Data.Models.KeyAllocationMethod.MacAddress: + key = await KeyService.Allocate(availableKey, keyRequest.MacAddress); + break; + + case Data.Models.KeyAllocationMethod.UserAccount: + key = await KeyService.Allocate(availableKey, user); + break; + } + + return Mapper.Map(key); } } - private async Task AllocateNewKey(Guid id, KeyRequest keyRequest) + /// + /// Allocate a new key using specified allocation method + /// + /// The ID of the game + /// + /// + /// Allocated key + private async Task AllocateNewKey(Guid id, KeyRequest keyRequest, Data.Models.KeyAllocationMethod keyAllocationMethod) { + var user = await UserManager.FindByNameAsync(User.Identity.Name); + var availableKey = KeyService.Get(k => k.Game.Id == id) .Where(k => (k.AllocationMethod == Data.Models.KeyAllocationMethod.MacAddress && String.IsNullOrWhiteSpace(k.ClaimedByMacAddress)) @@ -79,7 +146,12 @@ namespace LANCommander.Server.Controllers.Api if (availableKey == null) return null; - return Mapper.Map(await KeyService.Allocate(availableKey, keyRequest.MacAddress)); + if (keyAllocationMethod == Data.Models.KeyAllocationMethod.MacAddress) + return Mapper.Map(await KeyService.Allocate(availableKey, keyRequest.MacAddress)); + else if (keyAllocationMethod == Data.Models.KeyAllocationMethod.UserAccount) + return Mapper.Map(await KeyService.Allocate(availableKey, user)); + else + return null; } } } diff --git a/LANCommander.Server/Data/Models/Game.cs b/LANCommander.Server/Data/Models/Game.cs index 63717f29..9cf7af4e 100644 --- a/LANCommander.Server/Data/Models/Game.cs +++ b/LANCommander.Server/Data/Models/Game.cs @@ -21,6 +21,8 @@ namespace LANCommander.Server.Data.Models public virtual ICollection? Actions { get; set; } = new List(); + public KeyAllocationMethod KeyAllocationMethod { get; set; } = KeyAllocationMethod.UserAccount; + public GameType Type { get; set; } public Guid? BaseGameId { get; set; } [ForeignKey(nameof(BaseGameId))] diff --git a/LANCommander.Server/Migrations/20240904002809_AddGameKeyAllocationMethod.Designer.cs b/LANCommander.Server/Migrations/20240904002809_AddGameKeyAllocationMethod.Designer.cs new file mode 100644 index 00000000..cd698ec2 --- /dev/null +++ b/LANCommander.Server/Migrations/20240904002809_AddGameKeyAllocationMethod.Designer.cs @@ -0,0 +1,2475 @@ +// +using System; +using LANCommander.Server.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace LANCommander.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20240904002809_AddGameKeyAllocationMethod")] + partial class AddGameKeyAllocationMethod + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.7") + .HasAnnotation("Proxies:ChangeTracking", false) + .HasAnnotation("Proxies:CheckEquality", false) + .HasAnnotation("Proxies:LazyLoading", true); + + modelBuilder.Entity("CategoryGame", b => + { + b.Property("CategoriesId") + .HasColumnType("TEXT"); + + b.Property("GamesId") + .HasColumnType("TEXT"); + + b.HasKey("CategoriesId", "GamesId"); + + b.HasIndex("GamesId"); + + b.ToTable("CategoryGame"); + }); + + modelBuilder.Entity("CollectionGame", b => + { + b.Property("CollectionId") + .HasColumnType("TEXT"); + + b.Property("GameId") + .HasColumnType("TEXT"); + + b.HasKey("CollectionId", "GameId"); + + b.HasIndex("GameId"); + + b.ToTable("CollectionGame"); + }); + + modelBuilder.Entity("GameDeveloper", b => + { + b.Property("DeveloperId") + .HasColumnType("TEXT"); + + b.Property("GameId") + .HasColumnType("TEXT"); + + b.HasKey("DeveloperId", "GameId"); + + b.HasIndex("GameId"); + + b.ToTable("GameDeveloper"); + }); + + modelBuilder.Entity("GameGenre", b => + { + b.Property("GamesId") + .HasColumnType("TEXT"); + + b.Property("GenresId") + .HasColumnType("TEXT"); + + b.HasKey("GamesId", "GenresId"); + + b.HasIndex("GenresId"); + + b.ToTable("GameGenre"); + }); + + modelBuilder.Entity("GamePlatform", b => + { + b.Property("GamesId") + .HasColumnType("TEXT"); + + b.Property("PlatformsId") + .HasColumnType("TEXT"); + + b.HasKey("GamesId", "PlatformsId"); + + b.HasIndex("PlatformsId"); + + b.ToTable("GamePlatform"); + }); + + modelBuilder.Entity("GamePublisher", b => + { + b.Property("GameId") + .HasColumnType("TEXT"); + + b.Property("PublisherId") + .HasColumnType("TEXT"); + + b.HasKey("GameId", "PublisherId"); + + b.HasIndex("PublisherId"); + + b.ToTable("GamePublisher"); + }); + + modelBuilder.Entity("GameRedistributable", b => + { + b.Property("GameId") + .HasColumnType("TEXT"); + + b.Property("RedistributableId") + .HasColumnType("TEXT"); + + b.HasKey("GameId", "RedistributableId"); + + b.HasIndex("RedistributableId"); + + b.ToTable("GameRedistributable"); + }); + + modelBuilder.Entity("GameTag", b => + { + b.Property("GamesId") + .HasColumnType("TEXT"); + + b.Property("TagsId") + .HasColumnType("TEXT"); + + b.HasKey("GamesId", "TagsId"); + + b.HasIndex("TagsId"); + + b.ToTable("GameTag"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Action", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Arguments") + .HasColumnType("TEXT"); + + b.Property("CreatedById") + .HasColumnType("TEXT"); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("GameId") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Path") + .HasColumnType("TEXT"); + + b.Property("PrimaryAction") + .HasColumnType("INTEGER"); + + b.Property("ServerId") + .HasColumnType("TEXT"); + + b.Property("SortOrder") + .HasColumnType("INTEGER"); + + b.Property("UpdatedById") + .HasColumnType("TEXT"); + + b.Property("UpdatedOn") + .HasColumnType("TEXT"); + + b.Property("WorkingDirectory") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedById"); + + b.HasIndex("GameId"); + + b.HasIndex("ServerId"); + + b.HasIndex("UpdatedById"); + + b.ToTable("Actions"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Archive", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Changelog") + .HasColumnType("TEXT"); + + b.Property("CompressedSize") + .HasColumnType("INTEGER"); + + b.Property("CreatedById") + .HasColumnType("TEXT"); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("GameId") + .HasColumnType("TEXT"); + + b.Property("LastVersionId") + .HasColumnType("TEXT"); + + b.Property("ObjectKey") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RedistributableId") + .HasColumnType("TEXT"); + + b.Property("UncompressedSize") + .HasColumnType("INTEGER"); + + b.Property("UpdatedById") + .HasColumnType("TEXT"); + + b.Property("UpdatedOn") + .HasColumnType("TEXT"); + + b.Property("Version") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedById"); + + b.HasIndex("GameId"); + + b.HasIndex("LastVersionId"); + + b.HasIndex("RedistributableId"); + + b.HasIndex("UpdatedById"); + + b.ToTable("Archive"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Category", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedById") + .HasColumnType("TEXT"); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ParentId") + .HasColumnType("TEXT"); + + b.Property("UpdatedById") + .HasColumnType("TEXT"); + + b.Property("UpdatedOn") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedById"); + + b.HasIndex("ParentId"); + + b.HasIndex("UpdatedById"); + + b.ToTable("Categories"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Collection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedById") + .HasColumnType("TEXT"); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedById") + .HasColumnType("TEXT"); + + b.Property("UpdatedOn") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedById"); + + b.HasIndex("UpdatedById"); + + b.ToTable("Collections"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Company", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedById") + .HasColumnType("TEXT"); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedById") + .HasColumnType("TEXT"); + + b.Property("UpdatedOn") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedById"); + + b.HasIndex("UpdatedById"); + + b.ToTable("Companies"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Engine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedById") + .HasColumnType("TEXT"); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedById") + .HasColumnType("TEXT"); + + b.Property("UpdatedOn") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedById"); + + b.HasIndex("UpdatedById"); + + b.ToTable("Engines"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Game", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("BaseGameId") + .HasColumnType("TEXT"); + + b.Property("CreatedById") + .HasColumnType("TEXT"); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("DirectoryName") + .HasColumnType("TEXT"); + + b.Property("EngineId") + .HasColumnType("TEXT"); + + b.Property("IGDBId") + .HasColumnType("INTEGER"); + + b.Property("KeyAllocationMethod") + .HasColumnType("INTEGER"); + + b.Property("Notes") + .HasColumnType("TEXT"); + + b.Property("ReleasedOn") + .HasColumnType("TEXT"); + + b.Property("Singleplayer") + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT"); + + b.Property("Title") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UpdatedById") + .HasColumnType("TEXT"); + + b.Property("UpdatedOn") + .HasColumnType("TEXT"); + + b.Property("ValidKeyRegex") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("BaseGameId"); + + b.HasIndex("CreatedById"); + + b.HasIndex("EngineId"); + + b.HasIndex("UpdatedById"); + + b.ToTable("Games"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.GameSave", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedById") + .HasColumnType("TEXT"); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("GameId") + .HasColumnType("TEXT"); + + b.Property("UpdatedById") + .HasColumnType("TEXT"); + + b.Property("UpdatedOn") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedById"); + + b.HasIndex("GameId"); + + b.HasIndex("UpdatedById"); + + b.HasIndex("UserId"); + + b.ToTable("GameSaves"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Genre", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedById") + .HasColumnType("TEXT"); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedById") + .HasColumnType("TEXT"); + + b.Property("UpdatedOn") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedById"); + + b.HasIndex("UpdatedById"); + + b.ToTable("Genres"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Issue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedById") + .HasColumnType("TEXT"); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("Description") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("GameId") + .HasColumnType("TEXT"); + + b.Property("ResolvedById") + .HasColumnType("TEXT"); + + b.Property("ResolvedOn") + .HasColumnType("TEXT"); + + b.Property("UpdatedById") + .HasColumnType("TEXT"); + + b.Property("UpdatedOn") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedById"); + + b.HasIndex("GameId"); + + b.HasIndex("ResolvedById"); + + b.HasIndex("UpdatedById"); + + b.ToTable("Issues"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Key", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AllocationMethod") + .HasColumnType("INTEGER"); + + b.Property("ClaimedByComputerName") + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("ClaimedByIpv4Address") + .HasMaxLength(15) + .HasColumnType("TEXT"); + + b.Property("ClaimedByMacAddress") + .HasMaxLength(17) + .HasColumnType("TEXT"); + + b.Property("ClaimedByUserId") + .HasColumnType("TEXT"); + + b.Property("ClaimedOn") + .HasColumnType("TEXT"); + + b.Property("CreatedById") + .HasColumnType("TEXT"); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("GameId") + .HasColumnType("TEXT"); + + b.Property("UpdatedById") + .HasColumnType("TEXT"); + + b.Property("UpdatedOn") + .HasColumnType("TEXT"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ClaimedByUserId"); + + b.HasIndex("CreatedById"); + + b.HasIndex("GameId"); + + b.HasIndex("UpdatedById"); + + b.ToTable("Keys"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Media", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Crc32") + .IsRequired() + .HasMaxLength(8) + .HasColumnType("TEXT"); + + b.Property("CreatedById") + .HasColumnType("TEXT"); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("FileId") + .HasColumnType("TEXT"); + + b.Property("GameId") + .HasColumnType("TEXT"); + + b.Property("MimeType") + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(64) + .HasColumnType("TEXT"); + + b.Property("SourceUrl") + .HasMaxLength(2048) + .HasColumnType("TEXT"); + + b.Property("ThumbnailId") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UpdatedById") + .HasColumnType("TEXT"); + + b.Property("UpdatedOn") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedById"); + + b.HasIndex("GameId"); + + b.HasIndex("ThumbnailId") + .IsUnique(); + + b.HasIndex("UpdatedById"); + + b.HasIndex("UserId"); + + b.ToTable("Media"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.MultiplayerMode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedById") + .HasColumnType("TEXT"); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("GameId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("MaxPlayers") + .HasColumnType("INTEGER"); + + b.Property("MinPlayers") + .HasColumnType("INTEGER"); + + b.Property("NetworkProtocol") + .HasColumnType("INTEGER"); + + b.Property("Spectators") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UpdatedById") + .HasColumnType("TEXT"); + + b.Property("UpdatedOn") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedById"); + + b.HasIndex("GameId"); + + b.HasIndex("UpdatedById"); + + b.ToTable("MultiplayerModes"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Page", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Contents") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedById") + .HasColumnType("TEXT"); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("ParentId") + .HasColumnType("TEXT"); + + b.Property("Route") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("TEXT"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("SortOrder") + .HasColumnType("INTEGER"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("UpdatedById") + .HasColumnType("TEXT"); + + b.Property("UpdatedOn") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedById"); + + b.HasIndex("ParentId"); + + b.HasIndex("UpdatedById"); + + b.ToTable("Pages"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Platform", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedById") + .HasColumnType("TEXT"); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedById") + .HasColumnType("TEXT"); + + b.Property("UpdatedOn") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedById"); + + b.HasIndex("UpdatedById"); + + b.ToTable("Platforms"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.PlaySession", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedById") + .HasColumnType("TEXT"); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("End") + .HasColumnType("TEXT"); + + b.Property("GameId") + .HasColumnType("TEXT"); + + b.Property("Start") + .HasColumnType("TEXT"); + + b.Property("UpdatedById") + .HasColumnType("TEXT"); + + b.Property("UpdatedOn") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedById"); + + b.HasIndex("GameId"); + + b.HasIndex("UpdatedById"); + + b.HasIndex("UserId"); + + b.ToTable("PlaySessions"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Redistributable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedById") + .HasColumnType("TEXT"); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Notes") + .HasColumnType("TEXT"); + + b.Property("UpdatedById") + .HasColumnType("TEXT"); + + b.Property("UpdatedOn") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedById"); + + b.HasIndex("UpdatedById"); + + b.ToTable("Redistributables"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.SavePath", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedById") + .HasColumnType("TEXT"); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("GameId") + .HasColumnType("TEXT"); + + b.Property("IsRegex") + .HasColumnType("INTEGER"); + + b.Property("Path") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UpdatedById") + .HasColumnType("TEXT"); + + b.Property("UpdatedOn") + .HasColumnType("TEXT"); + + b.Property("WorkingDirectory") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedById"); + + b.HasIndex("GameId"); + + b.HasIndex("UpdatedById"); + + b.ToTable("SavePaths"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Script", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Contents") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedById") + .HasColumnType("TEXT"); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("GameId") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RedistributableId") + .HasColumnType("TEXT"); + + b.Property("RequiresAdmin") + .HasColumnType("INTEGER"); + + b.Property("ServerId") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UpdatedById") + .HasColumnType("TEXT"); + + b.Property("UpdatedOn") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedById"); + + b.HasIndex("GameId"); + + b.HasIndex("RedistributableId"); + + b.HasIndex("ServerId"); + + b.HasIndex("UpdatedById"); + + b.ToTable("Scripts"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Server", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Arguments") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Autostart") + .HasColumnType("INTEGER"); + + b.Property("AutostartDelay") + .HasColumnType("INTEGER"); + + b.Property("AutostartMethod") + .HasColumnType("INTEGER"); + + b.Property("CreatedById") + .HasColumnType("TEXT"); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("GameId") + .HasColumnType("TEXT"); + + b.Property("Host") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("OnStartScriptPath") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("OnStopScriptPath") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Path") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("ProcessTerminationMethod") + .HasColumnType("INTEGER"); + + b.Property("UpdatedById") + .HasColumnType("TEXT"); + + b.Property("UpdatedOn") + .HasColumnType("TEXT"); + + b.Property("UseShellExecute") + .HasColumnType("INTEGER"); + + b.Property("WorkingDirectory") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedById"); + + b.HasIndex("GameId"); + + b.HasIndex("UpdatedById"); + + b.ToTable("Servers"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.ServerConsole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedById") + .HasColumnType("TEXT"); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("Host") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Password") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Path") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("ServerId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ServerId1") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UpdatedById") + .HasColumnType("TEXT"); + + b.Property("UpdatedOn") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedById"); + + b.HasIndex("ServerId"); + + b.HasIndex("ServerId1"); + + b.HasIndex("UpdatedById"); + + b.ToTable("ServerConsoles"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.ServerHttpPath", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedById") + .HasColumnType("TEXT"); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("LocalPath") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Path") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ServerId") + .HasColumnType("TEXT"); + + b.Property("ServerId1") + .HasColumnType("TEXT"); + + b.Property("UpdatedById") + .HasColumnType("TEXT"); + + b.Property("UpdatedOn") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedById"); + + b.HasIndex("ServerId"); + + b.HasIndex("ServerId1"); + + b.HasIndex("UpdatedById"); + + b.ToTable("ServerHttpPath"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Tag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedById") + .HasColumnType("TEXT"); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedById") + .HasColumnType("TEXT"); + + b.Property("UpdatedOn") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedById"); + + b.HasIndex("UpdatedById"); + + b.ToTable("Tags"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AccessFailedCount") + .HasColumnType("INTEGER"); + + b.Property("Alias") + .HasColumnType("TEXT"); + + b.Property("Approved") + .HasColumnType("INTEGER"); + + b.Property("ApprovedOn") + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EmailConfirmed") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnabled") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnd") + .HasColumnType("TEXT"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("PasswordHash") + .HasColumnType("TEXT"); + + b.Property("PhoneNumber") + .HasColumnType("TEXT"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("INTEGER"); + + b.Property("RefreshToken") + .HasColumnType("TEXT"); + + b.Property("RefreshTokenExpiration") + .HasColumnType("TEXT"); + + b.Property("SecurityStamp") + .HasColumnType("TEXT"); + + b.Property("TwoFactorEnabled") + .HasColumnType("INTEGER"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.UserCustomField", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedById") + .HasColumnType("TEXT"); + + b.Property("CreatedOn") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("TEXT"); + + b.Property("UpdatedById") + .HasColumnType("TEXT"); + + b.Property("UpdatedOn") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedById"); + + b.HasIndex("UpdatedById"); + + b.HasIndex("UserId"); + + b.ToTable("UserCustomField"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("ProviderKey") + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("ProviderDisplayName") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("PageGame", b => + { + b.Property("GameId") + .HasColumnType("TEXT"); + + b.Property("PageId") + .HasColumnType("TEXT"); + + b.HasKey("GameId", "PageId"); + + b.HasIndex("PageId"); + + b.ToTable("PageGame"); + }); + + modelBuilder.Entity("PageRedistributable", b => + { + b.Property("PageId") + .HasColumnType("TEXT"); + + b.Property("RedistributableId") + .HasColumnType("TEXT"); + + b.HasKey("PageId", "RedistributableId"); + + b.HasIndex("RedistributableId"); + + b.ToTable("PageRedistributable"); + }); + + modelBuilder.Entity("PageServer", b => + { + b.Property("PageId") + .HasColumnType("TEXT"); + + b.Property("ServerId") + .HasColumnType("TEXT"); + + b.HasKey("PageId", "ServerId"); + + b.HasIndex("ServerId"); + + b.ToTable("PageServer"); + }); + + modelBuilder.Entity("RoleCollection", b => + { + b.Property("CollectionId") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("CollectionId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("RoleCollection"); + }); + + modelBuilder.Entity("CategoryGame", b => + { + b.HasOne("LANCommander.Server.Data.Models.Category", null) + .WithMany() + .HasForeignKey("CategoriesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LANCommander.Server.Data.Models.Game", null) + .WithMany() + .HasForeignKey("GamesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("CollectionGame", b => + { + b.HasOne("LANCommander.Server.Data.Models.Collection", null) + .WithMany() + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LANCommander.Server.Data.Models.Game", null) + .WithMany() + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("GameDeveloper", b => + { + b.HasOne("LANCommander.Server.Data.Models.Company", null) + .WithMany() + .HasForeignKey("DeveloperId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LANCommander.Server.Data.Models.Game", null) + .WithMany() + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("GameGenre", b => + { + b.HasOne("LANCommander.Server.Data.Models.Game", null) + .WithMany() + .HasForeignKey("GamesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LANCommander.Server.Data.Models.Genre", null) + .WithMany() + .HasForeignKey("GenresId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("GamePlatform", b => + { + b.HasOne("LANCommander.Server.Data.Models.Game", null) + .WithMany() + .HasForeignKey("GamesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LANCommander.Server.Data.Models.Platform", null) + .WithMany() + .HasForeignKey("PlatformsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("GamePublisher", b => + { + b.HasOne("LANCommander.Server.Data.Models.Game", null) + .WithMany() + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LANCommander.Server.Data.Models.Company", null) + .WithMany() + .HasForeignKey("PublisherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("GameRedistributable", b => + { + b.HasOne("LANCommander.Server.Data.Models.Game", null) + .WithMany() + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LANCommander.Server.Data.Models.Redistributable", null) + .WithMany() + .HasForeignKey("RedistributableId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("GameTag", b => + { + b.HasOne("LANCommander.Server.Data.Models.Game", null) + .WithMany() + .HasForeignKey("GamesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LANCommander.Server.Data.Models.Tag", null) + .WithMany() + .HasForeignKey("TagsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Action", b => + { + b.HasOne("LANCommander.Server.Data.Models.User", "CreatedBy") + .WithMany() + .HasForeignKey("CreatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("LANCommander.Server.Data.Models.Game", "Game") + .WithMany("Actions") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("LANCommander.Server.Data.Models.Server", "Server") + .WithMany("Actions") + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("LANCommander.Server.Data.Models.User", "UpdatedBy") + .WithMany() + .HasForeignKey("UpdatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("CreatedBy"); + + b.Navigation("Game"); + + b.Navigation("Server"); + + b.Navigation("UpdatedBy"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Archive", b => + { + b.HasOne("LANCommander.Server.Data.Models.User", "CreatedBy") + .WithMany() + .HasForeignKey("CreatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("LANCommander.Server.Data.Models.Game", "Game") + .WithMany("Archives") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("LANCommander.Server.Data.Models.Archive", "LastVersion") + .WithMany() + .HasForeignKey("LastVersionId"); + + b.HasOne("LANCommander.Server.Data.Models.Redistributable", "Redistributable") + .WithMany("Archives") + .HasForeignKey("RedistributableId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("LANCommander.Server.Data.Models.User", "UpdatedBy") + .WithMany() + .HasForeignKey("UpdatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("CreatedBy"); + + b.Navigation("Game"); + + b.Navigation("LastVersion"); + + b.Navigation("Redistributable"); + + b.Navigation("UpdatedBy"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Category", b => + { + b.HasOne("LANCommander.Server.Data.Models.User", "CreatedBy") + .WithMany() + .HasForeignKey("CreatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("LANCommander.Server.Data.Models.Category", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("LANCommander.Server.Data.Models.User", "UpdatedBy") + .WithMany() + .HasForeignKey("UpdatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("CreatedBy"); + + b.Navigation("Parent"); + + b.Navigation("UpdatedBy"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Collection", b => + { + b.HasOne("LANCommander.Server.Data.Models.User", "CreatedBy") + .WithMany() + .HasForeignKey("CreatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("LANCommander.Server.Data.Models.User", "UpdatedBy") + .WithMany() + .HasForeignKey("UpdatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("CreatedBy"); + + b.Navigation("UpdatedBy"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Company", b => + { + b.HasOne("LANCommander.Server.Data.Models.User", "CreatedBy") + .WithMany() + .HasForeignKey("CreatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("LANCommander.Server.Data.Models.User", "UpdatedBy") + .WithMany() + .HasForeignKey("UpdatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("CreatedBy"); + + b.Navigation("UpdatedBy"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Engine", b => + { + b.HasOne("LANCommander.Server.Data.Models.User", "CreatedBy") + .WithMany() + .HasForeignKey("CreatedById"); + + b.HasOne("LANCommander.Server.Data.Models.User", "UpdatedBy") + .WithMany() + .HasForeignKey("UpdatedById"); + + b.Navigation("CreatedBy"); + + b.Navigation("UpdatedBy"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Game", b => + { + b.HasOne("LANCommander.Server.Data.Models.Game", "BaseGame") + .WithMany("DependentGames") + .HasForeignKey("BaseGameId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("LANCommander.Server.Data.Models.User", "CreatedBy") + .WithMany() + .HasForeignKey("CreatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("LANCommander.Server.Data.Models.Engine", "Engine") + .WithMany("Games") + .HasForeignKey("EngineId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("LANCommander.Server.Data.Models.User", "UpdatedBy") + .WithMany() + .HasForeignKey("UpdatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("BaseGame"); + + b.Navigation("CreatedBy"); + + b.Navigation("Engine"); + + b.Navigation("UpdatedBy"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.GameSave", b => + { + b.HasOne("LANCommander.Server.Data.Models.User", "CreatedBy") + .WithMany() + .HasForeignKey("CreatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("LANCommander.Server.Data.Models.Game", "Game") + .WithMany("GameSaves") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("LANCommander.Server.Data.Models.User", "UpdatedBy") + .WithMany() + .HasForeignKey("UpdatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("LANCommander.Server.Data.Models.User", "User") + .WithMany("GameSaves") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CreatedBy"); + + b.Navigation("Game"); + + b.Navigation("UpdatedBy"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Genre", b => + { + b.HasOne("LANCommander.Server.Data.Models.User", "CreatedBy") + .WithMany() + .HasForeignKey("CreatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("LANCommander.Server.Data.Models.User", "UpdatedBy") + .WithMany() + .HasForeignKey("UpdatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("CreatedBy"); + + b.Navigation("UpdatedBy"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Issue", b => + { + b.HasOne("LANCommander.Server.Data.Models.User", "CreatedBy") + .WithMany() + .HasForeignKey("CreatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("LANCommander.Server.Data.Models.Game", "Game") + .WithMany("Issues") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.SetNull) + .IsRequired(); + + b.HasOne("LANCommander.Server.Data.Models.User", "ResolvedBy") + .WithMany() + .HasForeignKey("ResolvedById"); + + b.HasOne("LANCommander.Server.Data.Models.User", "UpdatedBy") + .WithMany() + .HasForeignKey("UpdatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("CreatedBy"); + + b.Navigation("Game"); + + b.Navigation("ResolvedBy"); + + b.Navigation("UpdatedBy"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Key", b => + { + b.HasOne("LANCommander.Server.Data.Models.User", "ClaimedByUser") + .WithMany() + .HasForeignKey("ClaimedByUserId"); + + b.HasOne("LANCommander.Server.Data.Models.User", "CreatedBy") + .WithMany() + .HasForeignKey("CreatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("LANCommander.Server.Data.Models.Game", "Game") + .WithMany("Keys") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LANCommander.Server.Data.Models.User", "UpdatedBy") + .WithMany() + .HasForeignKey("UpdatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("ClaimedByUser"); + + b.Navigation("CreatedBy"); + + b.Navigation("Game"); + + b.Navigation("UpdatedBy"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Media", b => + { + b.HasOne("LANCommander.Server.Data.Models.User", "CreatedBy") + .WithMany() + .HasForeignKey("CreatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("LANCommander.Server.Data.Models.Game", "Game") + .WithMany("Media") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("LANCommander.Server.Data.Models.Media", "Thumbnail") + .WithOne("Parent") + .HasForeignKey("LANCommander.Server.Data.Models.Media", "ThumbnailId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("LANCommander.Server.Data.Models.User", "UpdatedBy") + .WithMany() + .HasForeignKey("UpdatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("LANCommander.Server.Data.Models.User", "User") + .WithMany("Media") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("CreatedBy"); + + b.Navigation("Game"); + + b.Navigation("Thumbnail"); + + b.Navigation("UpdatedBy"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.MultiplayerMode", b => + { + b.HasOne("LANCommander.Server.Data.Models.User", "CreatedBy") + .WithMany() + .HasForeignKey("CreatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("LANCommander.Server.Data.Models.Game", "Game") + .WithMany("MultiplayerModes") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LANCommander.Server.Data.Models.User", "UpdatedBy") + .WithMany() + .HasForeignKey("UpdatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("CreatedBy"); + + b.Navigation("Game"); + + b.Navigation("UpdatedBy"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Page", b => + { + b.HasOne("LANCommander.Server.Data.Models.User", "CreatedBy") + .WithMany() + .HasForeignKey("CreatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("LANCommander.Server.Data.Models.Page", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("LANCommander.Server.Data.Models.User", "UpdatedBy") + .WithMany() + .HasForeignKey("UpdatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("CreatedBy"); + + b.Navigation("Parent"); + + b.Navigation("UpdatedBy"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Platform", b => + { + b.HasOne("LANCommander.Server.Data.Models.User", "CreatedBy") + .WithMany() + .HasForeignKey("CreatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("LANCommander.Server.Data.Models.User", "UpdatedBy") + .WithMany() + .HasForeignKey("UpdatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("CreatedBy"); + + b.Navigation("UpdatedBy"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.PlaySession", b => + { + b.HasOne("LANCommander.Server.Data.Models.User", "CreatedBy") + .WithMany() + .HasForeignKey("CreatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("LANCommander.Server.Data.Models.Game", "Game") + .WithMany("PlaySessions") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("LANCommander.Server.Data.Models.User", "UpdatedBy") + .WithMany() + .HasForeignKey("UpdatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("LANCommander.Server.Data.Models.User", "User") + .WithMany("PlaySessions") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CreatedBy"); + + b.Navigation("Game"); + + b.Navigation("UpdatedBy"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Redistributable", b => + { + b.HasOne("LANCommander.Server.Data.Models.User", "CreatedBy") + .WithMany() + .HasForeignKey("CreatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("LANCommander.Server.Data.Models.User", "UpdatedBy") + .WithMany() + .HasForeignKey("UpdatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("CreatedBy"); + + b.Navigation("UpdatedBy"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.SavePath", b => + { + b.HasOne("LANCommander.Server.Data.Models.User", "CreatedBy") + .WithMany() + .HasForeignKey("CreatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("LANCommander.Server.Data.Models.Game", "Game") + .WithMany("SavePaths") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("LANCommander.Server.Data.Models.User", "UpdatedBy") + .WithMany() + .HasForeignKey("UpdatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("CreatedBy"); + + b.Navigation("Game"); + + b.Navigation("UpdatedBy"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Script", b => + { + b.HasOne("LANCommander.Server.Data.Models.User", "CreatedBy") + .WithMany() + .HasForeignKey("CreatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("LANCommander.Server.Data.Models.Game", "Game") + .WithMany("Scripts") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("LANCommander.Server.Data.Models.Redistributable", "Redistributable") + .WithMany("Scripts") + .HasForeignKey("RedistributableId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("LANCommander.Server.Data.Models.Server", "Server") + .WithMany("Scripts") + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("LANCommander.Server.Data.Models.User", "UpdatedBy") + .WithMany() + .HasForeignKey("UpdatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("CreatedBy"); + + b.Navigation("Game"); + + b.Navigation("Redistributable"); + + b.Navigation("Server"); + + b.Navigation("UpdatedBy"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Server", b => + { + b.HasOne("LANCommander.Server.Data.Models.User", "CreatedBy") + .WithMany() + .HasForeignKey("CreatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("LANCommander.Server.Data.Models.Game", "Game") + .WithMany("Servers") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("LANCommander.Server.Data.Models.User", "UpdatedBy") + .WithMany() + .HasForeignKey("UpdatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("CreatedBy"); + + b.Navigation("Game"); + + b.Navigation("UpdatedBy"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.ServerConsole", b => + { + b.HasOne("LANCommander.Server.Data.Models.User", "CreatedBy") + .WithMany() + .HasForeignKey("CreatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("LANCommander.Server.Data.Models.Server", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LANCommander.Server.Data.Models.Server", null) + .WithMany("ServerConsoles") + .HasForeignKey("ServerId1"); + + b.HasOne("LANCommander.Server.Data.Models.User", "UpdatedBy") + .WithMany() + .HasForeignKey("UpdatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("CreatedBy"); + + b.Navigation("Server"); + + b.Navigation("UpdatedBy"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.ServerHttpPath", b => + { + b.HasOne("LANCommander.Server.Data.Models.User", "CreatedBy") + .WithMany() + .HasForeignKey("CreatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("LANCommander.Server.Data.Models.Server", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LANCommander.Server.Data.Models.Server", null) + .WithMany("HttpPaths") + .HasForeignKey("ServerId1"); + + b.HasOne("LANCommander.Server.Data.Models.User", "UpdatedBy") + .WithMany() + .HasForeignKey("UpdatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("CreatedBy"); + + b.Navigation("Server"); + + b.Navigation("UpdatedBy"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Tag", b => + { + b.HasOne("LANCommander.Server.Data.Models.User", "CreatedBy") + .WithMany() + .HasForeignKey("CreatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("LANCommander.Server.Data.Models.User", "UpdatedBy") + .WithMany() + .HasForeignKey("UpdatedById") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("CreatedBy"); + + b.Navigation("UpdatedBy"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.UserCustomField", b => + { + b.HasOne("LANCommander.Server.Data.Models.User", "CreatedBy") + .WithMany() + .HasForeignKey("CreatedById"); + + b.HasOne("LANCommander.Server.Data.Models.User", "UpdatedBy") + .WithMany() + .HasForeignKey("UpdatedById"); + + b.HasOne("LANCommander.Server.Data.Models.User", "User") + .WithMany("CustomFields") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CreatedBy"); + + b.Navigation("UpdatedBy"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("LANCommander.Server.Data.Models.Role", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("LANCommander.Server.Data.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("LANCommander.Server.Data.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("LANCommander.Server.Data.Models.Role", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LANCommander.Server.Data.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("LANCommander.Server.Data.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("PageGame", b => + { + b.HasOne("LANCommander.Server.Data.Models.Game", null) + .WithMany() + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LANCommander.Server.Data.Models.Page", null) + .WithMany() + .HasForeignKey("PageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("PageRedistributable", b => + { + b.HasOne("LANCommander.Server.Data.Models.Page", null) + .WithMany() + .HasForeignKey("PageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LANCommander.Server.Data.Models.Redistributable", null) + .WithMany() + .HasForeignKey("RedistributableId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("PageServer", b => + { + b.HasOne("LANCommander.Server.Data.Models.Page", null) + .WithMany() + .HasForeignKey("PageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LANCommander.Server.Data.Models.Server", null) + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("RoleCollection", b => + { + b.HasOne("LANCommander.Server.Data.Models.Collection", null) + .WithMany() + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LANCommander.Server.Data.Models.Role", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Category", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Engine", b => + { + b.Navigation("Games"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Game", b => + { + b.Navigation("Actions"); + + b.Navigation("Archives"); + + b.Navigation("DependentGames"); + + b.Navigation("GameSaves"); + + b.Navigation("Issues"); + + b.Navigation("Keys"); + + b.Navigation("Media"); + + b.Navigation("MultiplayerModes"); + + b.Navigation("PlaySessions"); + + b.Navigation("SavePaths"); + + b.Navigation("Scripts"); + + b.Navigation("Servers"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Media", b => + { + b.Navigation("Parent"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Page", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Redistributable", b => + { + b.Navigation("Archives"); + + b.Navigation("Scripts"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.Server", b => + { + b.Navigation("Actions"); + + b.Navigation("HttpPaths"); + + b.Navigation("Scripts"); + + b.Navigation("ServerConsoles"); + }); + + modelBuilder.Entity("LANCommander.Server.Data.Models.User", b => + { + b.Navigation("CustomFields"); + + b.Navigation("GameSaves"); + + b.Navigation("Media"); + + b.Navigation("PlaySessions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/LANCommander.Server/Migrations/20240904002809_AddGameKeyAllocationMethod.cs b/LANCommander.Server/Migrations/20240904002809_AddGameKeyAllocationMethod.cs new file mode 100644 index 00000000..7a5c725e --- /dev/null +++ b/LANCommander.Server/Migrations/20240904002809_AddGameKeyAllocationMethod.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace LANCommander.Migrations +{ + /// + public partial class AddGameKeyAllocationMethod : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "KeyAllocationMethod", + table: "Games", + type: "INTEGER", + nullable: false, + defaultValue: 0); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "KeyAllocationMethod", + table: "Games"); + } + } +} diff --git a/LANCommander.Server/Migrations/DatabaseContextModelSnapshot.cs b/LANCommander.Server/Migrations/DatabaseContextModelSnapshot.cs index 3b22a295..d6535e51 100644 --- a/LANCommander.Server/Migrations/DatabaseContextModelSnapshot.cs +++ b/LANCommander.Server/Migrations/DatabaseContextModelSnapshot.cs @@ -412,6 +412,9 @@ namespace LANCommander.Migrations b.Property("IGDBId") .HasColumnType("INTEGER"); + b.Property("KeyAllocationMethod") + .HasColumnType("INTEGER"); + b.Property("Notes") .HasColumnType("TEXT"); diff --git a/LANCommander.Server/UI/Pages/Games/Edit.razor b/LANCommander.Server/UI/Pages/Games/Edit.razor index e569b5f0..a5ba6688 100644 --- a/LANCommander.Server/UI/Pages/Games/Edit.razor +++ b/LANCommander.Server/UI/Pages/Games/Edit.razor @@ -160,6 +160,12 @@ } + + +