Add database models and migrations for redistributables

redistributables
Pat Hartl 2023-10-18 01:14:31 -05:00
parent 80bd7dc66c
commit a00d0b3b42
11 changed files with 1941 additions and 9 deletions

View File

@ -37,13 +37,13 @@ namespace LANCommander.Data
builder.Entity<Game>()
.HasMany(g => g.Archives)
.WithOne(g => g.Game)
.IsRequired(true)
.IsRequired(false)
.OnDelete(DeleteBehavior.Cascade);
builder.Entity<Game>()
.HasMany(g => g.Scripts)
.WithOne(s => s.Game)
.IsRequired(true)
.IsRequired(false)
.OnDelete(DeleteBehavior.Cascade);
builder.Entity<Game>()
@ -104,6 +104,18 @@ namespace LANCommander.Data
.WithOne(sl => sl.Server)
.IsRequired(true)
.OnDelete(DeleteBehavior.Cascade);
builder.Entity<Redistributable>()
.HasMany(r => r.Archives)
.WithOne(a => a.Redistributable)
.IsRequired(false)
.OnDelete(DeleteBehavior.Cascade);
builder.Entity<Redistributable>()
.HasMany(r => r.Scripts)
.WithOne(s => s.Redistributable)
.IsRequired(false)
.OnDelete(DeleteBehavior.Cascade);
}
public DbSet<Game>? Games { get; set; }
@ -123,5 +135,7 @@ namespace LANCommander.Data
public DbSet<Server>? Servers { get; set; }
public DbSet<ServerConsole>? ServerConsoles { get; set; }
public DbSet<Redistributable>? Redistributables { get; set; }
}
}

View File

@ -13,12 +13,18 @@ namespace LANCommander.Data.Models
[Required]
public string Version { get; set; }
public Guid GameId { get; set; }
public Guid? GameId { get; set; }
[JsonIgnore]
[ForeignKey(nameof(GameId))]
[InverseProperty("Archives")]
public virtual Game? Game { get; set; }
public Guid? RedistributableId { get; set; }
[JsonIgnore]
[ForeignKey(nameof(RedistributableId))]
[InverseProperty("Archives")]
public virtual Redistributable? Redistributable { get; set; }
[Display(Name = "Last Version")]
public virtual Archive? LastVersion { get; set; }

View File

@ -0,0 +1,15 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace LANCommander.Data.Models
{
[Table("Redistributables")]
public class Redistributable : BaseModel
{
public string Name { get; set; }
public string? Description { get; set; }
public string? Notes { get; set; }
public virtual ICollection<Archive>? Archives { get; set; }
public virtual ICollection<Script>? Scripts { get; set; }
}
}

View File

@ -18,5 +18,11 @@ namespace LANCommander.Data.Models
[ForeignKey(nameof(GameId))]
[InverseProperty("Scripts")]
public virtual Game? Game { get; set; }
public Guid? RedistributableId { get; set; }
[JsonIgnore]
[ForeignKey(nameof(RedistributableId))]
[InverseProperty("Scripts")]
public virtual Redistributable? Redistributable { get; set; }
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,158 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace LANCommander.Migrations
{
/// <inheritdoc />
public partial class AddRedistributables : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<Guid>(
name: "GameId",
table: "Scripts",
type: "TEXT",
nullable: true,
oldClrType: typeof(Guid),
oldType: "TEXT");
migrationBuilder.AddColumn<Guid>(
name: "RedistributableId",
table: "Scripts",
type: "TEXT",
nullable: true);
migrationBuilder.AlterColumn<Guid>(
name: "GameId",
table: "Archive",
type: "TEXT",
nullable: true,
oldClrType: typeof(Guid),
oldType: "TEXT");
migrationBuilder.AddColumn<Guid>(
name: "RedistributableId",
table: "Archive",
type: "TEXT",
nullable: true);
migrationBuilder.CreateTable(
name: "Redistributables",
columns: table => new
{
Id = table.Column<Guid>(type: "TEXT", nullable: false),
Name = table.Column<string>(type: "TEXT", nullable: false),
Description = table.Column<string>(type: "TEXT", nullable: true),
Notes = table.Column<string>(type: "TEXT", nullable: true),
CreatedOn = table.Column<DateTime>(type: "TEXT", nullable: false),
CreatedById = table.Column<Guid>(type: "TEXT", nullable: true),
UpdatedOn = table.Column<DateTime>(type: "TEXT", nullable: false),
UpdatedById = table.Column<Guid>(type: "TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Redistributables", x => x.Id);
table.ForeignKey(
name: "FK_Redistributables_AspNetUsers_CreatedById",
column: x => x.CreatedById,
principalTable: "AspNetUsers",
principalColumn: "Id");
table.ForeignKey(
name: "FK_Redistributables_AspNetUsers_UpdatedById",
column: x => x.UpdatedById,
principalTable: "AspNetUsers",
principalColumn: "Id");
});
migrationBuilder.CreateIndex(
name: "IX_Scripts_RedistributableId",
table: "Scripts",
column: "RedistributableId");
migrationBuilder.CreateIndex(
name: "IX_Archive_RedistributableId",
table: "Archive",
column: "RedistributableId");
migrationBuilder.CreateIndex(
name: "IX_Redistributables_CreatedById",
table: "Redistributables",
column: "CreatedById");
migrationBuilder.CreateIndex(
name: "IX_Redistributables_UpdatedById",
table: "Redistributables",
column: "UpdatedById");
migrationBuilder.AddForeignKey(
name: "FK_Archive_Redistributables_RedistributableId",
table: "Archive",
column: "RedistributableId",
principalTable: "Redistributables",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_Scripts_Redistributables_RedistributableId",
table: "Scripts",
column: "RedistributableId",
principalTable: "Redistributables",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Archive_Redistributables_RedistributableId",
table: "Archive");
migrationBuilder.DropForeignKey(
name: "FK_Scripts_Redistributables_RedistributableId",
table: "Scripts");
migrationBuilder.DropTable(
name: "Redistributables");
migrationBuilder.DropIndex(
name: "IX_Scripts_RedistributableId",
table: "Scripts");
migrationBuilder.DropIndex(
name: "IX_Archive_RedistributableId",
table: "Archive");
migrationBuilder.DropColumn(
name: "RedistributableId",
table: "Scripts");
migrationBuilder.DropColumn(
name: "RedistributableId",
table: "Archive");
migrationBuilder.AlterColumn<Guid>(
name: "GameId",
table: "Scripts",
type: "TEXT",
nullable: false,
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"),
oldClrType: typeof(Guid),
oldType: "TEXT",
oldNullable: true);
migrationBuilder.AlterColumn<Guid>(
name: "GameId",
table: "Archive",
type: "TEXT",
nullable: false,
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"),
oldClrType: typeof(Guid),
oldType: "TEXT",
oldNullable: true);
}
}
}

View File

@ -165,7 +165,7 @@ namespace LANCommander.Migrations
b.Property<DateTime>("CreatedOn")
.HasColumnType("TEXT");
b.Property<Guid>("GameId")
b.Property<Guid?>("GameId")
.HasColumnType("TEXT");
b.Property<Guid?>("LastVersionId")
@ -175,6 +175,9 @@ namespace LANCommander.Migrations
.IsRequired()
.HasColumnType("TEXT");
b.Property<Guid?>("RedistributableId")
.HasColumnType("TEXT");
b.Property<long>("UncompressedSize")
.HasColumnType("INTEGER");
@ -196,6 +199,8 @@ namespace LANCommander.Migrations
b.HasIndex("LastVersionId");
b.HasIndex("RedistributableId");
b.HasIndex("UpdatedById");
b.ToTable("Archive");
@ -505,6 +510,43 @@ namespace LANCommander.Migrations
b.ToTable("MultiplayerModes");
});
modelBuilder.Entity("LANCommander.Data.Models.Redistributable", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<Guid?>("CreatedById")
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedOn")
.HasColumnType("TEXT");
b.Property<string>("Description")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Notes")
.HasColumnType("TEXT");
b.Property<Guid?>("UpdatedById")
.HasColumnType("TEXT");
b.Property<DateTime>("UpdatedOn")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("CreatedById");
b.HasIndex("UpdatedById");
b.ToTable("Redistributables");
});
modelBuilder.Entity("LANCommander.Data.Models.Role", b =>
{
b.Property<Guid>("Id")
@ -591,13 +633,15 @@ namespace LANCommander.Migrations
.HasColumnType("TEXT");
b.Property<Guid?>("GameId")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<Guid?>("RedistributableId")
.HasColumnType("TEXT");
b.Property<bool>("RequiresAdmin")
.HasColumnType("INTEGER");
@ -616,6 +660,8 @@ namespace LANCommander.Migrations
b.HasIndex("GameId");
b.HasIndex("RedistributableId");
b.HasIndex("UpdatedById");
b.ToTable("Scripts");
@ -1074,13 +1120,17 @@ namespace LANCommander.Migrations
b.HasOne("LANCommander.Data.Models.Game", "Game")
.WithMany("Archives")
.HasForeignKey("GameId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("LANCommander.Data.Models.Archive", "LastVersion")
.WithMany()
.HasForeignKey("LastVersionId");
b.HasOne("LANCommander.Data.Models.Redistributable", "Redistributable")
.WithMany("Archives")
.HasForeignKey("RedistributableId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("LANCommander.Data.Models.User", "UpdatedBy")
.WithMany()
.HasForeignKey("UpdatedById");
@ -1091,6 +1141,8 @@ namespace LANCommander.Migrations
b.Navigation("LastVersion");
b.Navigation("Redistributable");
b.Navigation("UpdatedBy");
});
@ -1243,6 +1295,21 @@ namespace LANCommander.Migrations
b.Navigation("UpdatedBy");
});
modelBuilder.Entity("LANCommander.Data.Models.Redistributable", b =>
{
b.HasOne("LANCommander.Data.Models.User", "CreatedBy")
.WithMany()
.HasForeignKey("CreatedById");
b.HasOne("LANCommander.Data.Models.User", "UpdatedBy")
.WithMany()
.HasForeignKey("UpdatedById");
b.Navigation("CreatedBy");
b.Navigation("UpdatedBy");
});
modelBuilder.Entity("LANCommander.Data.Models.SavePath", b =>
{
b.HasOne("LANCommander.Data.Models.User", "CreatedBy")
@ -1273,8 +1340,12 @@ namespace LANCommander.Migrations
b.HasOne("LANCommander.Data.Models.Game", "Game")
.WithMany("Scripts")
.HasForeignKey("GameId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("LANCommander.Data.Models.Redistributable", "Redistributable")
.WithMany("Scripts")
.HasForeignKey("RedistributableId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("LANCommander.Data.Models.User", "UpdatedBy")
.WithMany()
@ -1284,6 +1355,8 @@ namespace LANCommander.Migrations
b.Navigation("Game");
b.Navigation("Redistributable");
b.Navigation("UpdatedBy");
});
@ -1426,6 +1499,13 @@ namespace LANCommander.Migrations
b.Navigation("Servers");
});
modelBuilder.Entity("LANCommander.Data.Models.Redistributable", b =>
{
b.Navigation("Archives");
b.Navigation("Scripts");
});
modelBuilder.Entity("LANCommander.Data.Models.Server", b =>
{
b.Navigation("ServerConsoles");

View File

@ -0,0 +1,115 @@
@page "/Redistributables"
@using Microsoft.EntityFrameworkCore;
@attribute [Authorize]
@inject RedistributableService RedistributableService
@inject NavigationManager NavigationManager
@inject IMessageService MessageService
<PageHeader Title="Redistributables">
<PageHeaderExtra>
<Space Direction="DirectionVHType.Horizontal">
<SpaceItem>
<Search Placeholder="Search" @bind-Value="Search" BindOnInput DebounceMilliseconds="150" OnChange="() => LoadData()" />
</SpaceItem>
<SpaceItem>
<Button OnClick="() => Add()" Type="@ButtonType.Primary">Add Redistributable</Button>
</SpaceItem>
</Space>
</PageHeaderExtra>
</PageHeader>
<TableColumnPicker @ref="Picker" Key="Redistributables" @bind-Visible="ColumnPickerVisible" />
<Table TItem="Redistributable" DataSource="@Redistributables" Loading="@Loading" PageSize="25" Responsive>
<PropertyColumn Property="r => r.Name" Sortable Hidden="@(Picker.IsColumnHidden("Name"))" />
<PropertyColumn Property="s => s.CreatedOn" Format="MM/dd/yyyy hh:mm tt" Sortable Hidden="@(Picker.IsColumnHidden("Created On"))" />
<PropertyColumn Property="s => s.CreatedBy" Sortable Hidden="@(Picker.IsColumnHidden("Created By"))">
@context.CreatedBy?.UserName
</PropertyColumn>
<PropertyColumn Property="g => g.UpdatedOn" Format="MM/dd/yyyy hh:mm tt" Sortable Hidden="@(Picker.IsColumnHidden("Updated On"))" />
<PropertyColumn Property="g => g.UpdatedBy" Sortable Hidden="@(Picker.IsColumnHidden("Updated By"))">
@context.UpdatedBy?.UserName
</PropertyColumn>
<ActionColumn Title="" Style="text-align: right; white-space: nowrap">
<TitleTemplate>
<div style="text-align: right">
<Button Icon="@IconType.Outline.Edit" Type="@ButtonType.Text" OnClick="() => OpenColumnPicker()" />
</div>
</TitleTemplate>
<ChildContent>
<Space Direction="DirectionVHType.Horizontal">
<SpaceItem>
<Button OnClick="() => Edit(context)">Edit</Button>
</SpaceItem>
<SpaceItem>
<Popconfirm OnConfirm="() => Delete(context)" Title="Are you sure you want to delete this redistributable?">
<Button Icon="@IconType.Outline.Close" Type="@ButtonType.Text" Danger />
</Popconfirm>
</SpaceItem>
</Space>
</ChildContent>
</ActionColumn>
</Table>
@code {
IEnumerable<Redistributable> Redistributables { get; set; } = new List<Redistributable>();
bool Loading = true;
string Search = "";
TableColumnPicker Picker;
bool ColumnPickerVisible = false;
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
LoadData();
Loading = false;
StateHasChanged();
}
}
private async Task LoadData()
{
var fuzzySearch = Search.ToLower().Trim();
Redistributables = await RedistributableService.Get(r => r.Name.ToLower().Contains(fuzzySearch)).OrderBy(r => r.Name).ToListAsync();
}
private void Add()
{
NavigationManager.NavigateTo("/Redistributables/Add");
}
private void Edit(Redistributable redistributable)
{
NavigationManager.NavigateTo($"/Redistributables/{redistributable.Id}/General");
}
private async Task Delete(Redistributable redistributable)
{
Redistributables = new List<Redistributable>();
Loading = true;
await RedistributableService.Delete(redistributable);
Redistributables = await RedistributableService.Get(x => true).OrderBy(r => r.Name).ToListAsync();
Loading = false;
}
private async Task OpenColumnPicker()
{
ColumnPickerVisible = true;
}
private async Task CloseColumnPicker()
{
ColumnPickerVisible = false;
}
}

View File

@ -141,6 +141,7 @@ namespace LANCommander
builder.Services.AddScoped<ServerService>();
builder.Services.AddScoped<ServerConsoleService>();
builder.Services.AddScoped<GameSaveService>();
builder.Services.AddScoped<RedistributableService>();
builder.Services.AddSingleton<ServerProcessService>();
builder.Services.AddSingleton<IPXRelayService>();

View File

@ -0,0 +1,12 @@
using LANCommander.Data;
using LANCommander.Data.Models;
namespace LANCommander.Services
{
public class RedistributableService : BaseDatabaseService<Redistributable>
{
public RedistributableService(DatabaseContext dbContext, IHttpContextAccessor httpContextAccessor) : base(dbContext, httpContextAccessor)
{
}
}
}

View File

@ -9,6 +9,7 @@
@if (User != null && User.IsInRole("Administrator"))
{
<MenuItem RouterLink="/Games">Games</MenuItem>
<MenuItem RouterLink="/Redistributables">Redistributables</MenuItem>
<MenuItem RouterLink="/Servers">Servers</MenuItem>
<MenuItem RouterLink="/Files">Files</MenuItem>
<MenuItem RouterLink="/Settings">Settings</MenuItem>