Various fixes for MySQL/MariaDB

- Regenerate migrations
- Fix identity database context lifetimes
- Fix rendering of edit components
- Move database settings to separate section
This commit is contained in:
Pat Hartl 2026-06-12 23:48:11 -05:00
parent e9854cc722
commit 790311ac8d
29 changed files with 2073 additions and 6462 deletions

View file

@ -1,213 +0,0 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace LANCommander.Server.Data.MySQL.Migrations
{
/// <inheritdoc />
public partial class FixServerShadowProperties : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_ServerConsoles_Servers_ServerId1",
table: "ServerConsoles");
migrationBuilder.DropForeignKey(
name: "FK_ServerHttpPath_Servers_ServerId1",
table: "ServerHttpPath");
migrationBuilder.DropIndex(
name: "IX_ServerHttpPath_ServerId1",
table: "ServerHttpPath");
migrationBuilder.DropIndex(
name: "IX_ServerConsoles_ServerId1",
table: "ServerConsoles");
migrationBuilder.DropColumn(
name: "ServerId1",
table: "ServerHttpPath");
migrationBuilder.DropColumn(
name: "ServerId1",
table: "ServerConsoles");
migrationBuilder.AlterColumn<string>(
name: "Path",
table: "ServerHttpPath",
type: "varchar(256)",
maxLength: 256,
nullable: false,
oldClrType: typeof(string),
oldType: "longtext")
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AlterColumn<string>(
name: "LocalPath",
table: "ServerHttpPath",
type: "varchar(1024)",
maxLength: 1024,
nullable: false,
oldClrType: typeof(string),
oldType: "longtext")
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AlterColumn<string>(
name: "Path",
table: "ServerConsoles",
type: "varchar(1024)",
maxLength: 1024,
nullable: false,
oldClrType: typeof(string),
oldType: "longtext")
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AlterColumn<string>(
name: "Password",
table: "ServerConsoles",
type: "varchar(128)",
maxLength: 128,
nullable: false,
oldClrType: typeof(string),
oldType: "longtext")
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AlterColumn<string>(
name: "Name",
table: "ServerConsoles",
type: "varchar(64)",
maxLength: 64,
nullable: false,
oldClrType: typeof(string),
oldType: "longtext")
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AlterColumn<string>(
name: "Host",
table: "ServerConsoles",
type: "varchar(256)",
maxLength: 256,
nullable: false,
oldClrType: typeof(string),
oldType: "longtext")
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "Path",
table: "ServerHttpPath",
type: "longtext",
nullable: false,
oldClrType: typeof(string),
oldType: "varchar(256)",
oldMaxLength: 256)
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AlterColumn<string>(
name: "LocalPath",
table: "ServerHttpPath",
type: "longtext",
nullable: false,
oldClrType: typeof(string),
oldType: "varchar(1024)",
oldMaxLength: 1024)
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AddColumn<Guid>(
name: "ServerId1",
table: "ServerHttpPath",
type: "char(36)",
nullable: true,
collation: "ascii_general_ci");
migrationBuilder.AlterColumn<string>(
name: "Path",
table: "ServerConsoles",
type: "longtext",
nullable: false,
oldClrType: typeof(string),
oldType: "varchar(1024)",
oldMaxLength: 1024)
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AlterColumn<string>(
name: "Password",
table: "ServerConsoles",
type: "longtext",
nullable: false,
oldClrType: typeof(string),
oldType: "varchar(128)",
oldMaxLength: 128)
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AlterColumn<string>(
name: "Name",
table: "ServerConsoles",
type: "longtext",
nullable: false,
oldClrType: typeof(string),
oldType: "varchar(64)",
oldMaxLength: 64)
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AlterColumn<string>(
name: "Host",
table: "ServerConsoles",
type: "longtext",
nullable: false,
oldClrType: typeof(string),
oldType: "varchar(256)",
oldMaxLength: 256)
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AddColumn<Guid>(
name: "ServerId1",
table: "ServerConsoles",
type: "char(36)",
nullable: true,
collation: "ascii_general_ci");
migrationBuilder.CreateIndex(
name: "IX_ServerHttpPath_ServerId1",
table: "ServerHttpPath",
column: "ServerId1");
migrationBuilder.CreateIndex(
name: "IX_ServerConsoles_ServerId1",
table: "ServerConsoles",
column: "ServerId1");
migrationBuilder.AddForeignKey(
name: "FK_ServerConsoles_Servers_ServerId1",
table: "ServerConsoles",
column: "ServerId1",
principalTable: "Servers",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_ServerHttpPath_Servers_ServerId1",
table: "ServerHttpPath",
column: "ServerId1",
principalTable: "Servers",
principalColumn: "Id");
}
}
}

View file

@ -1,29 +0,0 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace LANCommander.Server.Data.MySQL.Migrations
{
/// <inheritdoc />
public partial class AddMediaSortOrder : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "SortOrder",
table: "Media",
type: "int",
nullable: false,
defaultValue: 0);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "SortOrder",
table: "Media");
}
}
}

View file

@ -1,98 +0,0 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace LANCommander.Server.Data.MySQL.Migrations
{
/// <inheritdoc />
public partial class AddGameExternalIds : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "GameExternalIds",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
GameId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
Provider = table.Column<string>(type: "varchar(64)", maxLength: 64, nullable: false),
ExternalId = table.Column<string>(type: "varchar(256)", maxLength: 256, nullable: false),
CreatedOn = table.Column<DateTime>(type: "datetime(6)", nullable: false),
CreatedById = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
UpdatedOn = table.Column<DateTime>(type: "datetime(6)", nullable: false),
UpdatedById = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci")
},
constraints: table =>
{
table.PrimaryKey("PK_GameExternalIds", x => x.Id);
table.ForeignKey(
name: "FK_GameExternalIds_Games_GameId",
column: x => x.GameId,
principalTable: "Games",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_GameExternalIds_Users_CreatedById",
column: x => x.CreatedById,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "FK_GameExternalIds_Users_UpdatedById",
column: x => x.UpdatedById,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.CreateIndex(
name: "IX_GameExternalIds_CreatedById",
table: "GameExternalIds",
column: "CreatedById");
migrationBuilder.CreateIndex(
name: "IX_GameExternalIds_GameId",
table: "GameExternalIds",
column: "GameId");
migrationBuilder.CreateIndex(
name: "IX_GameExternalIds_UpdatedById",
table: "GameExternalIds",
column: "UpdatedById");
// Migrate existing IGDBId data
migrationBuilder.Sql(@"
INSERT INTO GameExternalIds (Id, GameId, Provider, ExternalId, CreatedOn, UpdatedOn)
SELECT UUID(), Id, 'IGDB', CAST(IGDBId AS CHAR), CreatedOn, UpdatedOn
FROM Games
WHERE IGDBId IS NOT NULL
");
migrationBuilder.DropColumn(
name: "IGDBId",
table: "Games");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<long>(
name: "IGDBId",
table: "Games",
type: "bigint",
nullable: true);
// Migrate data back
migrationBuilder.Sql(@"
UPDATE Games g
INNER JOIN GameExternalIds e ON e.GameId = g.Id AND e.Provider = 'IGDB'
SET g.IGDBId = CAST(e.ExternalId AS SIGNED)
");
migrationBuilder.DropTable(
name: "GameExternalIds");
}
}
}

View file

@ -108,6 +108,36 @@ namespace LANCommander.Server.Data.MySQL.Migrations
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "ChatThreads",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
Name = table.Column<string>(type: "varchar(64)", maxLength: 64, nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
CreatedOn = table.Column<DateTime>(type: "datetime(6)", nullable: false),
CreatedById = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
UpdatedOn = table.Column<DateTime>(type: "datetime(6)", nullable: false),
UpdatedById = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci")
},
constraints: table =>
{
table.PrimaryKey("PK_ChatThreads", x => x.Id);
table.ForeignKey(
name: "FK_ChatThreads_Users_CreatedById",
column: x => x.CreatedById,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "FK_ChatThreads_Users_UpdatedById",
column: x => x.UpdatedById,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.SetNull);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "Collections",
columns: table => new
@ -143,12 +173,12 @@ namespace LANCommander.Server.Data.MySQL.Migrations
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
Name = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
CreatedOn = table.Column<DateTime>(type: "datetime(6)", nullable: false),
CreatedById = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
UpdatedOn = table.Column<DateTime>(type: "datetime(6)", nullable: false),
UpdatedById = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci")
UpdatedById = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
Name = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4")
},
constraints: table =>
{
@ -261,50 +291,6 @@ namespace LANCommander.Server.Data.MySQL.Migrations
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "Pages",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
Title = table.Column<string>(type: "varchar(256)", maxLength: 256, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Slug = table.Column<string>(type: "varchar(256)", maxLength: 256, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Route = table.Column<string>(type: "varchar(2048)", maxLength: 2048, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Contents = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
SortOrder = table.Column<int>(type: "int", nullable: false),
ParentId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
CreatedOn = table.Column<DateTime>(type: "datetime(6)", nullable: false),
CreatedById = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
UpdatedOn = table.Column<DateTime>(type: "datetime(6)", nullable: false),
UpdatedById = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci")
},
constraints: table =>
{
table.PrimaryKey("PK_Pages", x => x.Id);
table.ForeignKey(
name: "FK_Pages_Pages_ParentId",
column: x => x.ParentId,
principalTable: "Pages",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_Pages_Users_CreatedById",
column: x => x.CreatedById,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "FK_Pages_Users_UpdatedById",
column: x => x.UpdatedById,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.SetNull);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "Platforms",
columns: table => new
@ -346,6 +332,8 @@ namespace LANCommander.Server.Data.MySQL.Migrations
.Annotation("MySql:CharSet", "utf8mb4"),
Notes = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
OptionSchema = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
CreatedOn = table.Column<DateTime>(type: "datetime(6)", nullable: false),
CreatedById = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
UpdatedOn = table.Column<DateTime>(type: "datetime(6)", nullable: false),
@ -463,6 +451,40 @@ namespace LANCommander.Server.Data.MySQL.Migrations
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "Tools",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
Name = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Description = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
Notes = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
CreatedOn = table.Column<DateTime>(type: "datetime(6)", nullable: false),
CreatedById = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
UpdatedOn = table.Column<DateTime>(type: "datetime(6)", nullable: false),
UpdatedById = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci")
},
constraints: table =>
{
table.PrimaryKey("PK_Tools", x => x.Id);
table.ForeignKey(
name: "FK_Tools_Users_CreatedById",
column: x => x.CreatedById,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "FK_Tools_Users_UpdatedById",
column: x => x.UpdatedById,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.SetNull);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "UserClaims",
columns: table => new
@ -574,12 +596,73 @@ namespace LANCommander.Server.Data.MySQL.Migrations
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "ChatMessages",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
ThreadId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
Content = table.Column<string>(type: "varchar(2000)", maxLength: 2000, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
CreatedOn = table.Column<DateTime>(type: "datetime(6)", nullable: false),
CreatedById = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
UpdatedOn = table.Column<DateTime>(type: "datetime(6)", nullable: false),
UpdatedById = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci")
},
constraints: table =>
{
table.PrimaryKey("PK_ChatMessages", x => x.Id);
table.ForeignKey(
name: "FK_ChatMessages_ChatThreads_ThreadId",
column: x => x.ThreadId,
principalTable: "ChatThreads",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_ChatMessages_Users_CreatedById",
column: x => x.CreatedById,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "FK_ChatMessages_Users_UpdatedById",
column: x => x.UpdatedById,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.SetNull);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "ChatThreadUser",
columns: table => new
{
ChatThreadsId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
ParticipantsId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci")
},
constraints: table =>
{
table.PrimaryKey("PK_ChatThreadUser", x => new { x.ChatThreadsId, x.ParticipantsId });
table.ForeignKey(
name: "FK_ChatThreadUser_ChatThreads_ChatThreadsId",
column: x => x.ChatThreadsId,
principalTable: "ChatThreads",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_ChatThreadUser_Users_ParticipantsId",
column: x => x.ParticipantsId,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "Games",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
IGDBId = table.Column<long>(type: "bigint", nullable: true),
Title = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
SortTitle = table.Column<string>(type: "longtext", nullable: true)
@ -633,31 +716,6 @@ namespace LANCommander.Server.Data.MySQL.Migrations
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "PageRedistributable",
columns: table => new
{
PageId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
RedistributableId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci")
},
constraints: table =>
{
table.PrimaryKey("PK_PageRedistributable", x => new { x.PageId, x.RedistributableId });
table.ForeignKey(
name: "FK_PageRedistributable_Pages_PageId",
column: x => x.PageId,
principalTable: "Pages",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_PageRedistributable_Redistributables_RedistributableId",
column: x => x.RedistributableId,
principalTable: "Redistributables",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "RoleClaims",
columns: table => new
@ -732,6 +790,88 @@ namespace LANCommander.Server.Data.MySQL.Migrations
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "Pages",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
Title = table.Column<string>(type: "varchar(256)", maxLength: 256, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Slug = table.Column<string>(type: "varchar(256)", maxLength: 256, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Route = table.Column<string>(type: "varchar(2048)", maxLength: 2048, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Contents = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
SortOrder = table.Column<int>(type: "int", nullable: false),
ParentId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
ToolId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
CreatedOn = table.Column<DateTime>(type: "datetime(6)", nullable: false),
CreatedById = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
UpdatedOn = table.Column<DateTime>(type: "datetime(6)", nullable: false),
UpdatedById = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci")
},
constraints: table =>
{
table.PrimaryKey("PK_Pages", x => x.Id);
table.ForeignKey(
name: "FK_Pages_Pages_ParentId",
column: x => x.ParentId,
principalTable: "Pages",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_Pages_Tools_ToolId",
column: x => x.ToolId,
principalTable: "Tools",
principalColumn: "Id");
table.ForeignKey(
name: "FK_Pages_Users_CreatedById",
column: x => x.CreatedById,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "FK_Pages_Users_UpdatedById",
column: x => x.UpdatedById,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.SetNull);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "ChatThreadReadStatuses",
columns: table => new
{
ThreadId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
UserId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
LastReadMessageId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci")
},
constraints: table =>
{
table.PrimaryKey("PK_ChatThreadReadStatuses", x => new { x.ThreadId, x.UserId });
table.ForeignKey(
name: "FK_ChatThreadReadStatuses_ChatMessages_LastReadMessageId",
column: x => x.LastReadMessageId,
principalTable: "ChatMessages",
principalColumn: "Id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "FK_ChatThreadReadStatuses_ChatThreads_ThreadId",
column: x => x.ThreadId,
principalTable: "ChatThreads",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_ChatThreadReadStatuses_Users_UserId",
column: x => x.UserId,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "Archive",
columns: table => new
@ -746,6 +886,7 @@ namespace LANCommander.Server.Data.MySQL.Migrations
StorageLocationId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
GameId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
RedistributableId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
ToolId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
LastVersionId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
UncompressedSize = table.Column<long>(type: "bigint", nullable: false),
CompressedSize = table.Column<long>(type: "bigint", nullable: false),
@ -780,6 +921,12 @@ namespace LANCommander.Server.Data.MySQL.Migrations
principalTable: "StorageLocations",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_Archive_Tools_ToolId",
column: x => x.ToolId,
principalTable: "Tools",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_Archive_Users_CreatedById",
column: x => x.CreatedById,
@ -906,6 +1053,45 @@ namespace LANCommander.Server.Data.MySQL.Migrations
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "GameExternalIds",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
GameId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
Provider = table.Column<string>(type: "varchar(64)", maxLength: 64, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
ExternalId = table.Column<string>(type: "varchar(256)", maxLength: 256, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
CreatedOn = table.Column<DateTime>(type: "datetime(6)", nullable: false),
CreatedById = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
UpdatedOn = table.Column<DateTime>(type: "datetime(6)", nullable: false),
UpdatedById = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci")
},
constraints: table =>
{
table.PrimaryKey("PK_GameExternalIds", x => x.Id);
table.ForeignKey(
name: "FK_GameExternalIds_Games_GameId",
column: x => x.GameId,
principalTable: "Games",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_GameExternalIds_Users_CreatedById",
column: x => x.CreatedById,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "FK_GameExternalIds_Users_UpdatedById",
column: x => x.UpdatedById,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.SetNull);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "GameGenre",
columns: table => new
@ -986,7 +1172,9 @@ namespace LANCommander.Server.Data.MySQL.Migrations
columns: table => new
{
GameId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
RedistributableId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci")
RedistributableId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
Options = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4")
},
constraints: table =>
{
@ -1081,6 +1269,31 @@ namespace LANCommander.Server.Data.MySQL.Migrations
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "GameTool",
columns: table => new
{
GamesId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
ToolsId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci")
},
constraints: table =>
{
table.PrimaryKey("PK_GameTool", x => new { x.GamesId, x.ToolsId });
table.ForeignKey(
name: "FK_GameTool_Games_GamesId",
column: x => x.GamesId,
principalTable: "Games",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_GameTool_Tools_ToolsId",
column: x => x.ToolsId,
principalTable: "Tools",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "Issues",
columns: table => new
@ -1133,7 +1346,7 @@ namespace LANCommander.Server.Data.MySQL.Migrations
Value = table.Column<string>(type: "varchar(255)", maxLength: 255, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
GameId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
AllocationMethod = table.Column<int>(type: "int", nullable: false),
AllocationMethod = table.Column<int>(type: "int", nullable: true),
ClaimedByMacAddress = table.Column<string>(type: "varchar(17)", maxLength: 17, nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
ClaimedByIpv4Address = table.Column<string>(type: "varchar(15)", maxLength: 15, nullable: true)
@ -1216,6 +1429,7 @@ namespace LANCommander.Server.Data.MySQL.Migrations
.Annotation("MySql:CharSet", "utf8mb4"),
Crc32 = table.Column<string>(type: "varchar(8)", maxLength: 8, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
SortOrder = table.Column<int>(type: "int", nullable: false),
ThumbnailId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
StorageLocationId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
GameId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
@ -1309,31 +1523,6 @@ namespace LANCommander.Server.Data.MySQL.Migrations
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "PageGame",
columns: table => new
{
GameId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
PageId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci")
},
constraints: table =>
{
table.PrimaryKey("PK_PageGame", x => new { x.GameId, x.PageId });
table.ForeignKey(
name: "FK_PageGame_Games_GameId",
column: x => x.GameId,
principalTable: "Games",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_PageGame_Pages_PageId",
column: x => x.PageId,
principalTable: "Pages",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "PlaySessions",
columns: table => new
@ -1378,6 +1567,44 @@ namespace LANCommander.Server.Data.MySQL.Migrations
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "Ratings",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
GameId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
Source = table.Column<string>(type: "varchar(128)", maxLength: 128, nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
Value = table.Column<float>(type: "float", nullable: false),
CreatedOn = table.Column<DateTime>(type: "datetime(6)", nullable: false),
CreatedById = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
UpdatedOn = table.Column<DateTime>(type: "datetime(6)", nullable: false),
UpdatedById = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci")
},
constraints: table =>
{
table.PrimaryKey("PK_Ratings", x => x.Id);
table.ForeignKey(
name: "FK_Ratings_Games_GameId",
column: x => x.GameId,
principalTable: "Games",
principalColumn: "Id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "FK_Ratings_Users_CreatedById",
column: x => x.CreatedById,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "FK_Ratings_Users_UpdatedById",
column: x => x.UpdatedById,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.SetNull);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "SavePaths",
columns: table => new
@ -1426,6 +1653,7 @@ namespace LANCommander.Server.Data.MySQL.Migrations
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
Name = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Engine = table.Column<int>(type: "int", nullable: false),
Path = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Arguments = table.Column<string>(type: "longtext", nullable: false)
@ -1436,6 +1664,11 @@ namespace LANCommander.Server.Data.MySQL.Migrations
.Annotation("MySql:CharSet", "utf8mb4"),
OnStopScriptPath = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
DockerHostId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
RemoteHostId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
RemoteServerId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
ContainerId = table.Column<string>(type: "varchar(64)", maxLength: 64, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Host = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Port = table.Column<int>(type: "int", nullable: false),
@ -1474,6 +1707,56 @@ namespace LANCommander.Server.Data.MySQL.Migrations
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "PageGame",
columns: table => new
{
GameId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
PageId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci")
},
constraints: table =>
{
table.PrimaryKey("PK_PageGame", x => new { x.GameId, x.PageId });
table.ForeignKey(
name: "FK_PageGame_Games_GameId",
column: x => x.GameId,
principalTable: "Games",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_PageGame_Pages_PageId",
column: x => x.PageId,
principalTable: "Pages",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "PageRedistributable",
columns: table => new
{
PageId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
RedistributableId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci")
},
constraints: table =>
{
table.PrimaryKey("PK_PageRedistributable", x => new { x.PageId, x.RedistributableId });
table.ForeignKey(
name: "FK_PageRedistributable_Pages_PageId",
column: x => x.PageId,
principalTable: "Pages",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_PageRedistributable_Redistributables_RedistributableId",
column: x => x.RedistributableId,
principalTable: "Redistributables",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "Actions",
columns: table => new
@ -1489,8 +1772,11 @@ namespace LANCommander.Server.Data.MySQL.Migrations
.Annotation("MySql:CharSet", "utf8mb4"),
PrimaryAction = table.Column<bool>(type: "tinyint(1)", nullable: false),
SortOrder = table.Column<int>(type: "int", nullable: false),
OptionOverrides = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
GameId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
ServerId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
ToolId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
CreatedOn = table.Column<DateTime>(type: "datetime(6)", nullable: false),
CreatedById = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
UpdatedOn = table.Column<DateTime>(type: "datetime(6)", nullable: false),
@ -1511,6 +1797,12 @@ namespace LANCommander.Server.Data.MySQL.Migrations
principalTable: "Servers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_Actions_Tools_ToolId",
column: x => x.ToolId,
principalTable: "Tools",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_Actions_Users_CreatedById",
column: x => x.CreatedById,
@ -1566,6 +1858,7 @@ namespace LANCommander.Server.Data.MySQL.Migrations
RequiresAdmin = table.Column<bool>(type: "tinyint(1)", nullable: false),
GameId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
RedistributableId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
ToolId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
ServerId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
CreatedOn = table.Column<DateTime>(type: "datetime(6)", nullable: false),
CreatedById = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
@ -1593,6 +1886,12 @@ namespace LANCommander.Server.Data.MySQL.Migrations
principalTable: "Servers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_Scripts_Tools_ToolId",
column: x => x.ToolId,
principalTable: "Tools",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_Scripts_Users_CreatedById",
column: x => x.CreatedById,
@ -1613,18 +1912,17 @@ namespace LANCommander.Server.Data.MySQL.Migrations
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
Name = table.Column<string>(type: "longtext", nullable: false)
Name = table.Column<string>(type: "varchar(64)", maxLength: 64, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Type = table.Column<int>(type: "int", nullable: false),
Path = table.Column<string>(type: "longtext", nullable: false)
Path = table.Column<string>(type: "varchar(1024)", maxLength: 1024, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Host = table.Column<string>(type: "longtext", nullable: false)
Host = table.Column<string>(type: "varchar(256)", maxLength: 256, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Port = table.Column<int>(type: "int", nullable: true),
Password = table.Column<string>(type: "longtext", nullable: false)
Password = table.Column<string>(type: "varchar(128)", maxLength: 128, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
ServerId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
ServerId1 = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
CreatedOn = table.Column<DateTime>(type: "datetime(6)", nullable: false),
CreatedById = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
UpdatedOn = table.Column<DateTime>(type: "datetime(6)", nullable: false),
@ -1639,11 +1937,6 @@ namespace LANCommander.Server.Data.MySQL.Migrations
principalTable: "Servers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_ServerConsoles_Servers_ServerId1",
column: x => x.ServerId1,
principalTable: "Servers",
principalColumn: "Id");
table.ForeignKey(
name: "FK_ServerConsoles_Users_CreatedById",
column: x => x.CreatedById,
@ -1664,12 +1957,11 @@ namespace LANCommander.Server.Data.MySQL.Migrations
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
LocalPath = table.Column<string>(type: "longtext", nullable: false)
LocalPath = table.Column<string>(type: "varchar(1024)", maxLength: 1024, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Path = table.Column<string>(type: "longtext", nullable: false)
Path = table.Column<string>(type: "varchar(256)", maxLength: 256, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
ServerId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
ServerId1 = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
CreatedOn = table.Column<DateTime>(type: "datetime(6)", nullable: false),
CreatedById = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
UpdatedOn = table.Column<DateTime>(type: "datetime(6)", nullable: false),
@ -1684,11 +1976,6 @@ namespace LANCommander.Server.Data.MySQL.Migrations
principalTable: "Servers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_ServerHttpPath_Servers_ServerId1",
column: x => x.ServerId1,
principalTable: "Servers",
principalColumn: "Id");
table.ForeignKey(
name: "FK_ServerHttpPath_Users_CreatedById",
column: x => x.CreatedById,
@ -1719,6 +2006,11 @@ namespace LANCommander.Server.Data.MySQL.Migrations
table: "Actions",
column: "ServerId");
migrationBuilder.CreateIndex(
name: "IX_Actions_ToolId",
table: "Actions",
column: "ToolId");
migrationBuilder.CreateIndex(
name: "IX_Actions_UpdatedById",
table: "Actions",
@ -1749,6 +2041,11 @@ namespace LANCommander.Server.Data.MySQL.Migrations
table: "Archive",
column: "StorageLocationId");
migrationBuilder.CreateIndex(
name: "IX_Archive_ToolId",
table: "Archive",
column: "ToolId");
migrationBuilder.CreateIndex(
name: "IX_Archive_UpdatedById",
table: "Archive",
@ -1774,6 +2071,46 @@ namespace LANCommander.Server.Data.MySQL.Migrations
table: "CategoryGame",
column: "GamesId");
migrationBuilder.CreateIndex(
name: "IX_ChatMessages_CreatedById",
table: "ChatMessages",
column: "CreatedById");
migrationBuilder.CreateIndex(
name: "IX_ChatMessages_ThreadId",
table: "ChatMessages",
column: "ThreadId");
migrationBuilder.CreateIndex(
name: "IX_ChatMessages_UpdatedById",
table: "ChatMessages",
column: "UpdatedById");
migrationBuilder.CreateIndex(
name: "IX_ChatThreadReadStatuses_LastReadMessageId",
table: "ChatThreadReadStatuses",
column: "LastReadMessageId");
migrationBuilder.CreateIndex(
name: "IX_ChatThreadReadStatuses_UserId",
table: "ChatThreadReadStatuses",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_ChatThreads_CreatedById",
table: "ChatThreads",
column: "CreatedById");
migrationBuilder.CreateIndex(
name: "IX_ChatThreads_UpdatedById",
table: "ChatThreads",
column: "UpdatedById");
migrationBuilder.CreateIndex(
name: "IX_ChatThreadUser_ParticipantsId",
table: "ChatThreadUser",
column: "ParticipantsId");
migrationBuilder.CreateIndex(
name: "IX_CollectionGame_GameId",
table: "CollectionGame",
@ -1829,6 +2166,21 @@ namespace LANCommander.Server.Data.MySQL.Migrations
table: "GameDeveloper",
column: "DeveloperId");
migrationBuilder.CreateIndex(
name: "IX_GameExternalIds_CreatedById",
table: "GameExternalIds",
column: "CreatedById");
migrationBuilder.CreateIndex(
name: "IX_GameExternalIds_GameId",
table: "GameExternalIds",
column: "GameId");
migrationBuilder.CreateIndex(
name: "IX_GameExternalIds_UpdatedById",
table: "GameExternalIds",
column: "UpdatedById");
migrationBuilder.CreateIndex(
name: "IX_GameGenre_GenresId",
table: "GameGenre",
@ -1899,6 +2251,11 @@ namespace LANCommander.Server.Data.MySQL.Migrations
table: "GameTag",
column: "TagsId");
migrationBuilder.CreateIndex(
name: "IX_GameTool_ToolsId",
table: "GameTool",
column: "ToolsId");
migrationBuilder.CreateIndex(
name: "IX_Genres_CreatedById",
table: "Genres",
@ -2036,6 +2393,11 @@ namespace LANCommander.Server.Data.MySQL.Migrations
table: "Pages",
column: "ParentId");
migrationBuilder.CreateIndex(
name: "IX_Pages_ToolId",
table: "Pages",
column: "ToolId");
migrationBuilder.CreateIndex(
name: "IX_Pages_UpdatedById",
table: "Pages",
@ -2076,6 +2438,21 @@ namespace LANCommander.Server.Data.MySQL.Migrations
table: "PlaySessions",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_Ratings_CreatedById",
table: "Ratings",
column: "CreatedById");
migrationBuilder.CreateIndex(
name: "IX_Ratings_GameId",
table: "Ratings",
column: "GameId");
migrationBuilder.CreateIndex(
name: "IX_Ratings_UpdatedById",
table: "Ratings",
column: "UpdatedById");
migrationBuilder.CreateIndex(
name: "IX_Redistributables_CreatedById",
table: "Redistributables",
@ -2147,6 +2524,11 @@ namespace LANCommander.Server.Data.MySQL.Migrations
table: "Scripts",
column: "ServerId");
migrationBuilder.CreateIndex(
name: "IX_Scripts_ToolId",
table: "Scripts",
column: "ToolId");
migrationBuilder.CreateIndex(
name: "IX_Scripts_UpdatedById",
table: "Scripts",
@ -2162,11 +2544,6 @@ namespace LANCommander.Server.Data.MySQL.Migrations
table: "ServerConsoles",
column: "ServerId");
migrationBuilder.CreateIndex(
name: "IX_ServerConsoles_ServerId1",
table: "ServerConsoles",
column: "ServerId1");
migrationBuilder.CreateIndex(
name: "IX_ServerConsoles_UpdatedById",
table: "ServerConsoles",
@ -2182,11 +2559,6 @@ namespace LANCommander.Server.Data.MySQL.Migrations
table: "ServerHttpPath",
column: "ServerId");
migrationBuilder.CreateIndex(
name: "IX_ServerHttpPath_ServerId1",
table: "ServerHttpPath",
column: "ServerId1");
migrationBuilder.CreateIndex(
name: "IX_ServerHttpPath_UpdatedById",
table: "ServerHttpPath",
@ -2227,6 +2599,16 @@ namespace LANCommander.Server.Data.MySQL.Migrations
table: "Tags",
column: "UpdatedById");
migrationBuilder.CreateIndex(
name: "IX_Tools_CreatedById",
table: "Tools",
column: "CreatedById");
migrationBuilder.CreateIndex(
name: "IX_Tools_UpdatedById",
table: "Tools",
column: "UpdatedById");
migrationBuilder.CreateIndex(
name: "IX_UserClaims_UserId",
table: "UserClaims",
@ -2291,6 +2673,12 @@ namespace LANCommander.Server.Data.MySQL.Migrations
migrationBuilder.DropTable(
name: "CategoryGame");
migrationBuilder.DropTable(
name: "ChatThreadReadStatuses");
migrationBuilder.DropTable(
name: "ChatThreadUser");
migrationBuilder.DropTable(
name: "CollectionGame");
@ -2300,6 +2688,9 @@ namespace LANCommander.Server.Data.MySQL.Migrations
migrationBuilder.DropTable(
name: "GameDeveloper");
migrationBuilder.DropTable(
name: "GameExternalIds");
migrationBuilder.DropTable(
name: "GameGenre");
@ -2318,6 +2709,9 @@ namespace LANCommander.Server.Data.MySQL.Migrations
migrationBuilder.DropTable(
name: "GameTag");
migrationBuilder.DropTable(
name: "GameTool");
migrationBuilder.DropTable(
name: "Issues");
@ -2345,6 +2739,9 @@ namespace LANCommander.Server.Data.MySQL.Migrations
migrationBuilder.DropTable(
name: "PlaySessions");
migrationBuilder.DropTable(
name: "Ratings");
migrationBuilder.DropTable(
name: "RoleClaims");
@ -2381,6 +2778,9 @@ namespace LANCommander.Server.Data.MySQL.Migrations
migrationBuilder.DropTable(
name: "Categories");
migrationBuilder.DropTable(
name: "ChatMessages");
migrationBuilder.DropTable(
name: "Genres");
@ -2414,6 +2814,12 @@ namespace LANCommander.Server.Data.MySQL.Migrations
migrationBuilder.DropTable(
name: "Roles");
migrationBuilder.DropTable(
name: "ChatThreads");
migrationBuilder.DropTable(
name: "Tools");
migrationBuilder.DropTable(
name: "Games");

View file

@ -36,11 +36,19 @@ namespace LANCommander.Server.Data
break;
case DatabaseProvider.MySQL:
optionsBuilder.UseMySql(ConnectionString, ServerVersion.AutoDetect(ConnectionString), options => options.MigrationsAssembly("LANCommander.Server.Data.MySQL"));
optionsBuilder.UseMySql(ConnectionString, ServerVersion.AutoDetect(ConnectionString), options =>
{
options.MigrationsAssembly("LANCommander.Server.Data.MySQL");
options.EnableRetryOnFailure(maxRetryCount: 3, maxRetryDelay: TimeSpan.FromSeconds(5), errorNumbersToAdd: null);
});
break;
case DatabaseProvider.PostgreSQL:
optionsBuilder.UseNpgsql(ConnectionString, options => options.MigrationsAssembly("LANCommander.Server.Data.PostgreSQL"));
optionsBuilder.UseNpgsql(ConnectionString, options =>
{
options.MigrationsAssembly("LANCommander.Server.Data.PostgreSQL");
options.EnableRetryOnFailure(maxRetryCount: 3, maxRetryDelay: TimeSpan.FromSeconds(5), errorCodesToAdd: null);
});
break;
}
}

View file

@ -1,4 +1,4 @@
using LANCommander.Server.Data;
using LANCommander.Server.Data;
using LANCommander.Server.Data.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
@ -61,23 +61,33 @@ namespace LANCommander.Server.Services.Factories
public void Dispose()
{
UserManager.Dispose();
RoleManager.Dispose();
DatabaseContext.Dispose();
}
}
public class IdentityContextFactory
{
private readonly DatabaseContext DatabaseContext;
private readonly IDbContextFactory<DatabaseContext> _dbContextFactory;
private readonly IServiceProvider ServiceProvider;
public IdentityContextFactory(DatabaseContext databaseContext, IServiceProvider serviceProvider)
public IdentityContextFactory(IDbContextFactory<DatabaseContext> dbContextFactory, IServiceProvider serviceProvider)
{
DatabaseContext = databaseContext;
_dbContextFactory = dbContextFactory;
ServiceProvider = serviceProvider;
}
public IdentityContext Create()
{
return new IdentityContext(DatabaseContext, ServiceProvider);
var context = _dbContextFactory.CreateDbContext();
return new IdentityContext(context, ServiceProvider);
}
public async Task<IdentityContext> CreateAsync()
{
var context = await _dbContextFactory.CreateDbContextAsync();
return new IdentityContext(context, ServiceProvider);
}
}
}

View file

@ -1,4 +1,4 @@
using AutoMapper;
using AutoMapper;
using LANCommander.Server.Data;
using LANCommander.Server.Data.Models;
using LANCommander.Server.Services.Factories;
@ -20,25 +20,18 @@ namespace LANCommander.Server.Services
IHttpContextAccessor httpContextAccessor,
IDbContextFactory<DatabaseContext> contextFactory,
CollectionService collectionService,
IdentityContextFactory identityContextFactory,
RoleManager<Role> roleManager) : BaseDatabaseService<Role>(logger, settingsProvider, cache, mapper, httpContextAccessor, contextFactory)
IdentityContextFactory identityContextFactory) : BaseDatabaseService<Role>(logger, settingsProvider, cache, mapper, httpContextAccessor, contextFactory)
{
public const string AdministratorRoleName = "Administrator";
private IdentityContext _identityContext;
public override void Initialize()
{
_identityContext = identityContextFactory.Create();
}
public override async Task<Role> AddAsync(Role role)
{
var result = await roleManager.CreateAsync(role);
using var identityContext = await identityContextFactory.CreateAsync();
var result = await identityContext.RoleManager.CreateAsync(role);
if (result.Succeeded)
return await GetAsync(role.Name);
throw new AddRoleException(result, "Could not create role");
}

View file

@ -3,6 +3,7 @@ using LANCommander.SDK.Enums;
using LANCommander.Server.Data;
using LANCommander.Server.Data.Enums;
using LANCommander.Server.Data.Models;
using LANCommander.Server.Services.Factories;
using LANCommander.Server.Settings.Enums;
using Microsoft.AspNetCore.Identity;
using Microsoft.Data.Sqlite;
@ -17,7 +18,7 @@ namespace LANCommander.Server.Services
public class SetupService(
ILogger<SetupService> logger,
SettingsProvider<Settings.Settings> settingsProvider,
UserManager<User> userManager,
IdentityContextFactory identityContextFactory,
IServiceProvider serviceProvider) : BaseService(logger, settingsProvider), IDisposable
{
public async Task<bool> IsSetupInitialized()
@ -26,15 +27,16 @@ namespace LANCommander.Server.Services
{
if (DatabaseContext.Provider == DatabaseProvider.Unknown)
return false;
var admins = await userManager.GetUsersInRoleAsync(RoleService.AdministratorRoleName);
using var identityContext = await identityContextFactory.CreateAsync();
var admins = await identityContext.UserManager.GetUsersInRoleAsync(RoleService.AdministratorRoleName);
return admins.Any();
}
catch (Exception ex)
{
logger.LogError(ex, ex.Message);
return false;
}
}

View file

@ -1,4 +1,4 @@
using AutoMapper;
using AutoMapper;
using AutoMapper.QueryableExtensions;
using LANCommander.Server.Data.Enums;
using LANCommander.Server.Data.Models;
@ -11,18 +11,20 @@ using System.Linq.Expressions;
using LANCommander.Server.Services.Abstractions;
using LANCommander.Server.Services.Exceptions;
using LANCommander.Server.Data;
using Microsoft.Extensions.Options;
using ZiggyCreatures.Caching.Fusion;
namespace LANCommander.Server.Services
{
public class UserService : BaseService, IBaseDatabaseService<User>
{
private readonly IdentityContext IdentityContext;
private readonly IdentityContextFactory _identityContextFactory;
private readonly CollectionService CollectionService;
private readonly IDbContextFactory<DatabaseContext> ContextFactory;
private readonly IMapper Mapper;
private readonly IFusionCache Cache;
private readonly IOptions<IdentityOptions> _identityOptions;
protected readonly List<Func<IQueryable<User>, IQueryable<User>>> _modifiers = new();
public UserService(
@ -32,19 +34,21 @@ namespace LANCommander.Server.Services
IFusionCache cache,
CollectionService collectionService,
IDbContextFactory<DatabaseContext> contextFactory,
IdentityContextFactory identityContextFactory) : base(logger, settingsProvider)
IdentityContextFactory identityContextFactory,
IOptions<IdentityOptions> identityOptions) : base(logger, settingsProvider)
{
IdentityContext = identityContextFactory.Create();
_identityContextFactory = identityContextFactory;
CollectionService = collectionService;
ContextFactory = contextFactory;
Mapper = mapper;
Cache = cache;
_identityOptions = identityOptions;
}
public void Reconfigure()
{
var options = IdentityContext.UserManager.Options;
if (options == null)
var options = _identityOptions.Value;
if (options == null)
return;
options.Password.RequireNonAlphanumeric = _settingsProvider.CurrentValue.Server.Authentication.PasswordRequireNonAlphanumeric;
@ -92,7 +96,7 @@ namespace LANCommander.Server.Services
public async Task<bool> IsInRoleAsync(User user, string roleName)
{
var roles = await GetRolesAsync(user);
return roles.Any(r => r.Name == roleName);
}
@ -116,10 +120,11 @@ namespace LANCommander.Server.Services
return new List<Collection>();
}
}
public async Task<bool> ExistsAsync(Expression<Func<User, bool>> predicate)
{
return await IdentityContext.DatabaseContext.Users.AnyAsync(predicate);
using var context = await ContextFactory.CreateDbContextAsync();
return await context.Users.AnyAsync(predicate);
}
public Task<User> AddAsync(User user)
@ -148,11 +153,15 @@ namespace LANCommander.Server.Services
}
else
{
result = await IdentityContext.UserManager.CreateAsync(user);
using var identityContext = await _identityContextFactory.CreateAsync();
result = await identityContext.UserManager.CreateAsync(user);
}
if (result.Succeeded)
return await IdentityContext.UserManager.FindByNameAsync(user.UserName);
{
using var findContext = await _identityContextFactory.CreateAsync();
return await findContext.UserManager.FindByNameAsync(user.UserName);
}
else
throw new UserRegistrationException(result, "Could not create user");
}
@ -161,15 +170,17 @@ namespace LANCommander.Server.Services
{
var user = await GetAsync(userName);
await IdentityContext.UserManager.AddToRoleAsync(user, roleName);
using var identityContext = await _identityContextFactory.CreateAsync();
await identityContext.UserManager.AddToRoleAsync(user, roleName);
}
public async Task AddToRolesAsync(string userName, IEnumerable<string> roleNames)
{
var user = await GetAsync(userName);
var result = await IdentityContext.UserManager.AddToRolesAsync(user, roleNames);
using var identityContext = await _identityContextFactory.CreateAsync();
var result = await identityContext.UserManager.AddToRolesAsync(user, roleNames);
await Cache.RemoveByTagAsync(["User/Security", "User/Roles", $"User/{user.Id}", $"Library/{user.Id}"]);
if (!result.Succeeded)
@ -180,20 +191,24 @@ namespace LANCommander.Server.Services
{
var user = await GetAsync(userName);
await IdentityContext.UserManager.RemoveFromRoleAsync(user, roleName);
using var identityContext = await _identityContextFactory.CreateAsync();
await identityContext.UserManager.RemoveFromRoleAsync(user, roleName);
}
public async Task<bool> CheckPassword(string userName, string password)
{
var user = await GetAsync(userName);
return await IdentityContext.UserManager.CheckPasswordAsync(user, password);
using var identityContext = await _identityContextFactory.CreateAsync();
return await identityContext.UserManager.CheckPasswordAsync(user, password);
}
public async Task<IdentityResult> CheckRegister(User user, string password)
{
var registerErrors = new List<IdentityError>();
var userManager = IdentityContext.UserManager;
using var identityContext = await _identityContextFactory.CreateAsync();
var userManager = identityContext.UserManager;
foreach (var validator in userManager.UserValidators ?? [])
{
@ -222,7 +237,8 @@ namespace LANCommander.Server.Services
{
var user = await GetAsync(userName);
var result = await IdentityContext.UserManager.ChangePasswordAsync(user, currentPassword, newPassword);
using var identityContext = await _identityContextFactory.CreateAsync();
var result = await identityContext.UserManager.ChangePasswordAsync(user, currentPassword, newPassword);
return result;
}
@ -237,17 +253,19 @@ namespace LANCommander.Server.Services
IdentityResult result;
var user = await GetAsync(userName);
if (bypassPolicy && IdentityContext.UserManager.PasswordValidators.Any())
{
await IdentityContext.UserManager.RemovePasswordAsync(user);
using var identityContext = await _identityContextFactory.CreateAsync();
result = await IdentityContext.UserManager.AddPasswordAsync(user, newPassword);
if (bypassPolicy && identityContext.UserManager.PasswordValidators.Any())
{
await identityContext.UserManager.RemovePasswordAsync(user);
result = await identityContext.UserManager.AddPasswordAsync(user, newPassword);
}
else
{
var token = await IdentityContext.UserManager.GeneratePasswordResetTokenAsync(user);
var token = await identityContext.UserManager.GeneratePasswordResetTokenAsync(user);
result = await IdentityContext.UserManager.ResetPasswordAsync(user, token, newPassword);
result = await identityContext.UserManager.ResetPasswordAsync(user, token, newPassword);
}
return result;
@ -285,11 +303,12 @@ namespace LANCommander.Server.Services
{
try
{
var queryable = IdentityContext.UserManager.Users.AsQueryable();
using var context = await ContextFactory.CreateDbContextAsync();
var queryable = context.Users.AsQueryable();
foreach (var modifier in _modifiers)
queryable = modifier.Invoke(queryable);
return await queryable.ToListAsync();
}
finally
@ -302,14 +321,12 @@ namespace LANCommander.Server.Services
{
try
{
var queryable = IdentityContext
.UserManager
.Users
.AsQueryable();
using var context = await ContextFactory.CreateDbContextAsync();
var queryable = context.Users.AsQueryable();
foreach (var modifier in _modifiers)
queryable = modifier.Invoke(queryable);
return await queryable
.ProjectTo<T>(Mapper.ConfigurationProvider)
.ToListAsync();
@ -348,14 +365,12 @@ namespace LANCommander.Server.Services
{
try
{
var queryable = IdentityContext
.UserManager
.Users
.AsQueryable();
using var context = await ContextFactory.CreateDbContextAsync();
var queryable = context.Users.AsQueryable();
foreach (var modifier in _modifiers)
queryable = modifier.Invoke(queryable);
return await queryable.Where(predicate).ToListAsync();
}
finally
@ -368,14 +383,12 @@ namespace LANCommander.Server.Services
{
try
{
var queryable = IdentityContext
.UserManager
.Users
.AsQueryable();
using var context = await ContextFactory.CreateDbContextAsync();
var queryable = context.Users.AsQueryable();
foreach (var modifier in _modifiers)
queryable = modifier.Invoke(queryable);
return await queryable
.Where(predicate)
.ProjectTo<T>(Mapper.ConfigurationProvider)
@ -391,14 +404,12 @@ namespace LANCommander.Server.Services
{
try
{
var queryable = IdentityContext
.UserManager
.Users
.AsQueryable();
using var context = await ContextFactory.CreateDbContextAsync();
var queryable = context.Users.AsQueryable();
foreach (var modifier in _modifiers)
queryable = modifier.Invoke(queryable);
return await queryable.FirstOrDefaultAsync(predicate);
}
finally
@ -411,14 +422,12 @@ namespace LANCommander.Server.Services
{
try
{
var queryable = IdentityContext
.UserManager
.Users
.AsQueryable();
using var context = await ContextFactory.CreateDbContextAsync();
var queryable = context.Users.AsQueryable();
foreach (var modifier in _modifiers)
queryable = modifier.Invoke(queryable);
return await queryable
.Where(predicate)
.ProjectTo<T>(Mapper.ConfigurationProvider)
@ -434,14 +443,12 @@ namespace LANCommander.Server.Services
{
try
{
var queryable = IdentityContext
.UserManager
.Users
.AsQueryable();
using var context = await ContextFactory.CreateDbContextAsync();
var queryable = context.Users.AsQueryable();
foreach (var modifier in _modifiers)
queryable = modifier.Invoke(queryable);
return await queryable
.Where(predicate)
.OrderBy(orderKeySelector)
@ -457,14 +464,12 @@ namespace LANCommander.Server.Services
{
try
{
var queryable = IdentityContext
.UserManager
.Users
.AsQueryable();
using var context = await ContextFactory.CreateDbContextAsync();
var queryable = context.Users.AsQueryable();
foreach (var modifier in _modifiers)
queryable = modifier.Invoke(queryable);
return await queryable
.Where(predicate)
.ProjectTo<T>(Mapper.ConfigurationProvider)
@ -479,28 +484,23 @@ namespace LANCommander.Server.Services
public async Task<bool> ExistsAsync(Guid id)
{
var user = await IdentityContext
.UserManager
.FindByIdAsync(id.ToString());
return user != null;
using var context = await ContextFactory.CreateDbContextAsync();
return await context.Users.AnyAsync(u => u.Id == id);
}
public async Task<ExistingEntityResult<User>> AddMissingAsync(Expression<Func<User, bool>> predicate, User entity)
{
var result = new ExistingEntityResult<User>();
var user = await IdentityContext
.UserManager
.Users
.FirstOrDefaultAsync(predicate);
var user = await FirstOrDefaultAsync(predicate);
if (user == null)
{
await IdentityContext.UserManager.CreateAsync(entity);
using var identityContext = await _identityContextFactory.CreateAsync();
await identityContext.UserManager.CreateAsync(entity);
result.Existing = false;
result.Value = await IdentityContext.UserManager.FindByNameAsync(user.UserName);
result.Value = await identityContext.UserManager.FindByNameAsync(entity.UserName);
}
else
{
@ -513,9 +513,9 @@ namespace LANCommander.Server.Services
public async Task<User> UpdateAsync(User entity)
{
var user = await IdentityContext
.UserManager
.FindByIdAsync(entity.Id.ToString());
using var identityContext = await _identityContextFactory.CreateAsync();
var user = await identityContext.UserManager.FindByIdAsync(entity.Id.ToString());
user.UserName = entity.UserName;
user.PhoneNumber = entity.PhoneNumber;
@ -525,20 +525,20 @@ namespace LANCommander.Server.Services
user.Approved = entity.Approved;
user.ApprovedOn = entity.ApprovedOn;
await IdentityContext.UserManager.UpdateAsync(user);
await identityContext.UserManager.UpdateAsync(user);
return user;
}
public async Task DeleteAsync(User entity)
{
var user = await IdentityContext
.UserManager
.FindByIdAsync(entity.Id.ToString());
using var identityContext = await _identityContextFactory.CreateAsync();
await IdentityContext.UserManager.DeleteAsync(user);
var user = await identityContext.UserManager.FindByIdAsync(entity.Id.ToString());
await identityContext.UserManager.DeleteAsync(user);
}
public IBaseDatabaseService<User> AsNoTracking()
{
return Query((queryable) =>
@ -592,7 +592,7 @@ namespace LANCommander.Server.Services
});
}
}
protected void Reset()
{
_modifiers.Clear();

View file

@ -271,7 +271,7 @@ namespace LANCommander.Server.Services
public abstract Task<T> AddAsync(T entity);
protected async Task<T> AddAsync(T addedEntity, Action<UpdateEntityContext<T>> additionalMapping = null)
protected async Task<T> AddAsync(T addedEntity, Func<UpdateEntityContext<T>, Task> additionalMapping = null)
{
try
{
@ -290,7 +290,7 @@ namespace LANCommander.Server.Services
{
var updateContext = new UpdateEntityContext<T>(context, newEntity, addedEntity);
additionalMapping?.Invoke(updateContext);
await additionalMapping(updateContext);
}
newEntity = (await context.AddAsync(newEntity)).Entity;
@ -339,7 +339,7 @@ namespace LANCommander.Server.Services
public abstract Task<T> UpdateAsync(T entity);
protected async Task<T> UpdateAsync(T updatedEntity, Action<UpdateEntityContext<T>> additionalMapping = null)
protected async Task<T> UpdateAsync(T updatedEntity, Func<UpdateEntityContext<T>, Task> additionalMapping = null)
{
using var context = await dbContextFactory.CreateDbContextAsync();
@ -353,8 +353,8 @@ namespace LANCommander.Server.Services
if (additionalMapping != null)
{
var updateContext = new UpdateEntityContext<T>(context, existingEntity, updatedEntity);
additionalMapping?.Invoke(updateContext);
await additionalMapping(updateContext);
}
var currentUser = await GetCurrentUserAsync(context);

View file

@ -72,6 +72,7 @@
</TitleTemplate>
<ChildContent>
<MenuItem RouterLink="/Settings/General">General</MenuItem>
<MenuItem RouterLink="/Settings/Database">Database</MenuItem>
<MenuItem RouterLink="/Settings/Users">Users</MenuItem>
<MenuItem RouterLink="/Settings/Roles">Roles</MenuItem>
<MenuItem RouterLink="/Settings/Authentication">Authentication</MenuItem>

View file

@ -56,9 +56,12 @@
</CascadingValue>
<div class="panel-layout-content">
<CascadingValue Value="Game.Id" Name="GameId">
@ChildContent?.Invoke(Game)
</CascadingValue>
@if (Game?.Id == Id)
{
<CascadingValue Value="Game.Id" Name="GameId">
@ChildContent?.Invoke(Game)
</CascadingValue>
}
</div>
</Content>
</Layout>

View file

@ -5,7 +5,7 @@
<TitleExtraTemplate>
<Space Direction="SpaceDirection.Horizontal">
<SpaceItem>
<Button Type="ButtonType.Primary" OnClick="_actionEditor.Save">Save</Button>
<Button Type="ButtonType.Primary" OnClick="() => _actionEditor?.Save()">Save</Button>
</SpaceItem>
</Space>
</TitleExtraTemplate>

View file

@ -4,7 +4,7 @@
<GameEditView Id="Id" Title="Custom Fields">
<TitleExtraTemplate>
<Flex Gap="FlexGap.Small" Justify="FlexJustify.End">
<Button Type="ButtonType.Primary" OnClick="_customFieldEditor.Save">Save</Button>
<Button Type="ButtonType.Primary" OnClick="() => _customFieldEditor?.Save()">Save</Button>
</Flex>
</TitleExtraTemplate>

View file

@ -4,7 +4,7 @@
<GameEditView Id="Id" Title="Multiplayer">
<TitleExtraTemplate>
<Flex Gap="FlexGap.Small" Justify="FlexJustify.End">
<Button Type="ButtonType.Primary" OnClick="_multiplayerModeEditor.Save">Save</Button>
<Button Type="ButtonType.Primary" OnClick="() => _multiplayerModeEditor?.Save()">Save</Button>
</Flex>
</TitleExtraTemplate>

View file

@ -4,7 +4,7 @@
<GameEditView Id="Id" Title="Save Paths">
<TitleExtraTemplate>
<Flex Gap="FlexGap.Small" Justify="FlexJustify.End">
<Button Type="ButtonType.Primary" OnClick="_savePathEditor.Save">Save</Button>
<Button Type="ButtonType.Primary" OnClick="() => _savePathEditor?.Save()">Save</Button>
</Flex>
</TitleExtraTemplate>

View file

@ -42,9 +42,12 @@
</CascadingValue>
<div class="panel-layout-content">
<CascadingValue Value="Redistributable.Id" Name="RedistributableId">
@ChildContent?.Invoke(Redistributable)
</CascadingValue>
@if (Redistributable?.Id == Id)
{
<CascadingValue Value="Redistributable.Id" Name="RedistributableId">
@ChildContent?.Invoke(Redistributable)
</CascadingValue>
}
</div>
</Content>
</Layout>

View file

@ -40,9 +40,12 @@
</CascadingValue>
<div class="panel-layout-content">
<CascadingValue Value="Server.Id" Name="ServerId">
@ChildContent?.Invoke(Server)
</CascadingValue>
@if (Server?.Id == Id)
{
<CascadingValue Value="Server.Id" Name="ServerId">
@ChildContent?.Invoke(Server)
</CascadingValue>
}
</div>
</Content>
</Layout>

View file

@ -8,7 +8,7 @@
{
<ServerControl ServerId="context.Id"/>
<Button Type="ButtonType.Primary" OnClick="_actionEditor.Save">Save</Button>
<Button Type="ButtonType.Primary" OnClick="() => _actionEditor?.Save()">Save</Button>
}
</Flex>
</TitleExtraTemplate>

View file

@ -8,7 +8,7 @@
<Flex Align="FlexAlign.Center" Justify="FlexJustify.End" Wrap="FlexWrap.NoWrap" Gap="FlexGap.Small">
<ServerControl ServerId="context.Id" />
<Button Type="ButtonType.Primary" OnClick="_serverConsoleEditor.Save">Save</Button>
<Button Type="ButtonType.Primary" OnClick="() => _serverConsoleEditor?.Save()">Save</Button>
</Flex>
}
</TitleExtraTemplate>

View file

@ -11,7 +11,7 @@
<Flex Align="FlexAlign.Center" Justify="FlexJustify.End" Wrap="FlexWrap.NoWrap" Gap="FlexGap.Small">
<ServerControl ServerId="context.Id" />
<Button Type="ButtonType.Primary" OnClick="_httpPathEditor.Save">Save</Button>
<Button Type="ButtonType.Primary" OnClick="() => _httpPathEditor?.Save()">Save</Button>
</Flex>
}
</TitleExtraTemplate>

View file

@ -0,0 +1,123 @@
@page "/Settings/Database"
@using LANCommander.SDK
@using LANCommander.Server.Settings
@using LANCommander.Server.Settings.Enums
@using Microsoft.Data.Sqlite
@using Microsoft.Extensions.Options
@using MySqlConnector
@using Npgsql
@inject IOptions<Settings> Settings
@inject SettingsProvider<Settings> SettingsProvider
@inject SetupService SetupService
@inject IMessageService MessageService
@inject ILogger<Database> Logger
@attribute [Authorize(Roles = RoleService.AdministratorRoleName)]
<PageHeader Title="Database" />
<PageContent>
<Form Model="Settings.Value" Loading="Loading" OnFinish="ValidateDatabaseConnection" Layout="@FormLayout.Vertical">
<FormItem Label="Database Provider">
<Select
@bind-Value="context.Server.Database.Provider"
TItem="DatabaseProvider"
TItemValue="DatabaseProvider"
DataSource="Enum.GetValues<DatabaseProvider>()"
OnSelectedItemChanged="OnDatabaseProviderChanged">
<LabelTemplate Context="Value">@Value.GetDisplayName()</LabelTemplate>
<ItemTemplate Context="Value">@Value.GetDisplayName()</ItemTemplate>
</Select>
</FormItem>
<FormItem Label="Connection String">
<Input @bind-Value="context.Server.Database.ConnectionString" />
</FormItem>
<FormItem>
<Button Type="@ButtonType.Primary" HtmlType="submit">
Test &amp; Save
</Button>
</FormItem>
</Form>
</PageContent>
@code {
bool Loading = false;
void OnDatabaseProviderChanged()
{
switch (Settings.Value.Server.Database.Provider)
{
case DatabaseProvider.SQLite:
var dbPath = AppPaths.GetConfigPath("LANCommander.db");
Settings.Value.Server.Database.ConnectionString = $"Data Source={dbPath};Cache=Shared";
break;
case DatabaseProvider.MySQL:
Settings.Value.Server.Database.ConnectionString = "Server=localhost;Uid=root;Pwd=password;Database=LANCommander";
break;
case DatabaseProvider.PostgreSQL:
Settings.Value.Server.Database.ConnectionString = "Host=localhost;Port=5432;Database=LANCommander;User Id=postgres;Password=password";
break;
}
}
async Task ValidateDatabaseConnection()
{
bool valid = false;
Loading = true;
StateHasChanged();
await Task.Yield();
try
{
SetupService.ValidateConnectionString(Settings.Value.Server.Database.Provider, Settings.Value.Server.Database.ConnectionString);
valid = true;
}
catch (SqliteException ex)
{
Logger?.LogError(ex, "Could not use SQLite database connection");
MessageService.Error($"Could not use SQLite database: {ex.Message}", 10);
}
catch (MySqlException ex)
{
Logger?.LogError(ex, "Could not connect to MySQL database");
MessageService.Error($"Could not connect to MySQL database: {ex.Message}", 10);
}
catch (NpgsqlException ex)
{
Logger?.LogError(ex, "Could not connect to PostgreSQL database");
MessageService.Error($"Could not connect to PostgreSQL database: {ex.Message}", 10);
}
catch (Exception ex)
{
Logger?.LogError(ex, "An unknown error occurred trying to access the database");
MessageService.Error("Could not validate the connection string!");
}
if (valid)
{
try
{
await SetupService.ChangeProviderAsync(Settings.Value.Server.Database.Provider, Settings.Value.Server.Database.ConnectionString);
SettingsProvider.Update(s =>
{
s.Server.Database.Provider = Settings.Value.Server.Database.Provider;
s.Server.Database.ConnectionString = Settings.Value.Server.Database.ConnectionString;
});
MessageService.Success("Database settings saved!");
}
catch (Exception ex)
{
Logger?.LogError(ex, "Could not initialize database!");
MessageService.Error($"Could not initialize database: {ex.Message}", 10);
}
}
Loading = false;
}
}

View file

@ -1,4 +1,4 @@
@page "/Settings"
@page "/Settings"
@page "/Settings/General"
@using LANCommander.Server.Settings
@using LANCommander.Server.Settings.Enums
@ -13,21 +13,10 @@
<PageContent>
<Form Model="Settings.Value" Layout="@FormLayout.Vertical">
<FormItem Label="Database Provider">
<Select @bind-Value="context.Server.Database.Provider" TItem="DatabaseProvider" TItemValue="DatabaseProvider" DataSource="Enum.GetValues<DatabaseProvider>()">
<LabelTemplate Context="Value">@Value.GetDisplayName()</LabelTemplate>
<ItemTemplate Context="Value">@Value.GetDisplayName()</ItemTemplate>
</Select>
</FormItem>
<FormItem Label="Database Connection String">
<InputPassword @bind-Value="context.Server.Database.ConnectionString" />
</FormItem>
<FormItem Label="Port">
<AntDesign.InputNumber @bind-Value="context.Server.Http.Port" />
</FormItem>
<Divider Text="SSL" />
<FormItem Label="Use SSL">
@ -61,10 +50,9 @@
{
SettingsProvider.Update(s =>
{
s.Server.Database = Settings.Value.Server.Database;
s.Server.Http = Settings.Value.Server.Http;
});
MessageService.Success("Settings saved!");
}
catch (Exception ex)

View file

@ -41,9 +41,12 @@
</CascadingValue>
<div class="panel-layout-content">
<CascadingValue Value="Tool.Id" Name="ToolId">
@ChildContent?.Invoke(Tool)
</CascadingValue>
@if (Tool?.Id == Id)
{
<CascadingValue Value="Tool.Id" Name="ToolId">
@ChildContent?.Invoke(Tool)
</CascadingValue>
}
</div>
</Content>
</Layout>

View file

@ -6,7 +6,7 @@
<TitleExtraTemplate>
<Space Direction="SpaceDirection.Horizontal">
<SpaceItem>
<Button Type="ButtonType.Primary" OnClick="_actionEditor.Save">Save</Button>
<Button Type="ButtonType.Primary" OnClick="() => _actionEditor?.Save()">Save</Button>
</SpaceItem>
</Space>
</TitleExtraTemplate>