1
0
mirror of https://github.com/chylex/Minecraft-Phantom-Panel.git synced 2024-10-18 15:42:50 +02:00

Compare commits

..

No commits in common. "3a17eee8d00bdb13d41e12e9e5f8bcadb5a0a6e0" and "956f1e779b2cd56ca7e97ec19c4c7bd4629cec5a" have entirely different histories.

221 changed files with 1529 additions and 2900 deletions

View File

@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Compile Bootstrap" type="js.build_tools.npm">
<package-json value="$PROJECT_DIR$/Web/Phantom.Web.Bootstrap/package.json" />
<package-json value="$PROJECT_DIR$/Server/Phantom.Server.Web.Bootstrap/package.json" />
<command value="run" />
<scripts>
<script value="compile" />

View File

@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Server" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/.artifacts/bin/Phantom.Controller/debug/Phantom.Controller.exe" />
<option name="EXE_PATH" value="$PROJECT_DIR$/.artifacts/bin/Phantom.Server/debug/Phantom.Server.exe" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/.workdir/Server" />
<option name="PASS_PARENT_ENVS" value="1" />
@ -17,7 +17,7 @@
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
<option name="RUNTIME_ARGUMENTS" value="" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/Controller/Phantom.Controller/Phantom.Controller.csproj" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/Server/Phantom.Server/Phantom.Server.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="0" />

View File

@ -5,4 +5,4 @@ if [%1]==[] (
exit
)
dotnet ef migrations add %~1 --project Controller/Phantom.Controller.Database.Postgres --msbuildprojectextensionspath .artifacts/obj/Phantom.Controller.Database.Postgres
dotnet ef migrations add %~1 --project Server/Phantom.Server.Database.Postgres --msbuildprojectextensionspath .artifacts/obj/Phantom.Server.Database.Postgres

View File

@ -3,4 +3,4 @@ if [ -z "$1" ]; then
exit 1
fi
dotnet ef migrations add "$1" --project Controller/Phantom.Controller.Database.Postgres --msbuildprojectextensionspath .artifacts/obj/Phantom.Controller.Database.Postgres
dotnet ef migrations add "$1" --project Server/Phantom.Server.Database.Postgres --msbuildprojectextensionspath .artifacts/obj/Phantom.Server.Database.Postgres

View File

@ -1,180 +0,0 @@
// <auto-generated />
using System;
using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Phantom.Controller.Database;
#nullable disable
namespace Phantom.Controller.Database.Postgres.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20231008122637_ReplaceIdentity")]
partial class ReplaceIdentity
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "7.0.11")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Phantom.Controller.Database.Entities.AgentEntity", b =>
{
b.Property<Guid>("AgentGuid")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("BuildVersion")
.IsRequired()
.HasColumnType("text");
b.Property<int>("MaxInstances")
.HasColumnType("integer");
b.Property<ushort>("MaxMemory")
.HasColumnType("integer");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<int>("ProtocolVersion")
.HasColumnType("integer");
b.HasKey("AgentGuid");
b.ToTable("Agents", "agents");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.AuditLogEntity", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<JsonDocument>("Data")
.HasColumnType("jsonb");
b.Property<string>("EventType")
.IsRequired()
.HasColumnType("text");
b.Property<string>("SubjectId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("SubjectType")
.IsRequired()
.HasColumnType("text");
b.Property<Guid?>("UserGuid")
.HasColumnType("uuid");
b.Property<DateTime>("UtcTime")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.ToTable("AuditLog", "system");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.EventLogEntity", b =>
{
b.Property<Guid>("EventGuid")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid?>("AgentGuid")
.HasColumnType("uuid");
b.Property<JsonDocument>("Data")
.HasColumnType("jsonb");
b.Property<string>("EventType")
.IsRequired()
.HasColumnType("text");
b.Property<string>("SubjectId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("SubjectType")
.IsRequired()
.HasColumnType("text");
b.Property<DateTime>("UtcTime")
.HasColumnType("timestamp with time zone");
b.HasKey("EventGuid");
b.ToTable("EventLog", "system");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.InstanceEntity", b =>
{
b.Property<Guid>("InstanceGuid")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid>("AgentGuid")
.HasColumnType("uuid");
b.Property<string>("InstanceName")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("JavaRuntimeGuid")
.HasColumnType("uuid");
b.Property<string>("JvmArguments")
.IsRequired()
.HasColumnType("text");
b.Property<bool>("LaunchAutomatically")
.HasColumnType("boolean");
b.Property<ushort>("MemoryAllocation")
.HasColumnType("integer");
b.Property<string>("MinecraftServerKind")
.IsRequired()
.HasColumnType("text");
b.Property<string>("MinecraftVersion")
.IsRequired()
.HasColumnType("text");
b.Property<int>("RconPort")
.HasColumnType("integer");
b.Property<int>("ServerPort")
.HasColumnType("integer");
b.HasKey("InstanceGuid");
b.ToTable("Instances", "agents");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.PermissionEntity", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Permissions", "identity");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -1,373 +0,0 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace Phantom.Controller.Database.Postgres.Migrations
{
/// <inheritdoc />
public partial class ReplaceIdentity : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_AuditLog_Users_UserId",
schema: "system",
table: "AuditLog");
migrationBuilder.DropTable(
name: "RoleClaims",
schema: "identity");
migrationBuilder.DropTable(
name: "RolePermissions",
schema: "identity");
migrationBuilder.DropTable(
name: "UserClaims",
schema: "identity");
migrationBuilder.DropTable(
name: "UserLogins",
schema: "identity");
migrationBuilder.DropTable(
name: "UserPermissions",
schema: "identity");
migrationBuilder.DropTable(
name: "UserRoles",
schema: "identity");
migrationBuilder.DropTable(
name: "UserTokens",
schema: "identity");
migrationBuilder.DropTable(
name: "Roles",
schema: "identity");
migrationBuilder.DropTable(
name: "Users",
schema: "identity");
migrationBuilder.DropIndex(
name: "IX_AuditLog_UserId",
schema: "system",
table: "AuditLog");
migrationBuilder.DropColumn(
name: "UserId",
schema: "system",
table: "AuditLog");
migrationBuilder.AddColumn<Guid>(
name: "UserGuid",
schema: "system",
table: "AuditLog",
type: "uuid",
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "UserGuid",
schema: "system",
table: "AuditLog");
migrationBuilder.AddColumn<string>(
name: "UserId",
schema: "system",
table: "AuditLog",
type: "text",
nullable: true);
migrationBuilder.CreateTable(
name: "Roles",
schema: "identity",
columns: table => new
{
Id = table.Column<string>(type: "text", nullable: false),
ConcurrencyStamp = table.Column<string>(type: "text", nullable: true),
Name = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
NormalizedName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Roles", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Users",
schema: "identity",
columns: table => new
{
Id = table.Column<string>(type: "text", nullable: false),
AccessFailedCount = table.Column<int>(type: "integer", nullable: false),
ConcurrencyStamp = table.Column<string>(type: "text", nullable: true),
Email = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
EmailConfirmed = table.Column<bool>(type: "boolean", nullable: false),
LockoutEnabled = table.Column<bool>(type: "boolean", nullable: false),
LockoutEnd = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
NormalizedEmail = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
NormalizedUserName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
PasswordHash = table.Column<string>(type: "text", nullable: true),
PhoneNumber = table.Column<string>(type: "text", nullable: true),
PhoneNumberConfirmed = table.Column<bool>(type: "boolean", nullable: false),
SecurityStamp = table.Column<string>(type: "text", nullable: true),
TwoFactorEnabled = table.Column<bool>(type: "boolean", nullable: false),
UserName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Users", x => x.Id);
});
migrationBuilder.CreateTable(
name: "RoleClaims",
schema: "identity",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
ClaimType = table.Column<string>(type: "text", nullable: true),
ClaimValue = table.Column<string>(type: "text", nullable: true),
RoleId = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_RoleClaims", x => x.Id);
table.ForeignKey(
name: "FK_RoleClaims_Roles_RoleId",
column: x => x.RoleId,
principalSchema: "identity",
principalTable: "Roles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "RolePermissions",
schema: "identity",
columns: table => new
{
RoleId = table.Column<string>(type: "text", nullable: false),
PermissionId = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_RolePermissions", x => new { x.RoleId, x.PermissionId });
table.ForeignKey(
name: "FK_RolePermissions_Permissions_PermissionId",
column: x => x.PermissionId,
principalSchema: "identity",
principalTable: "Permissions",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_RolePermissions_Roles_RoleId",
column: x => x.RoleId,
principalSchema: "identity",
principalTable: "Roles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "UserClaims",
schema: "identity",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
ClaimType = table.Column<string>(type: "text", nullable: true),
ClaimValue = table.Column<string>(type: "text", nullable: true),
UserId = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_UserClaims", x => x.Id);
table.ForeignKey(
name: "FK_UserClaims_Users_UserId",
column: x => x.UserId,
principalSchema: "identity",
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "UserLogins",
schema: "identity",
columns: table => new
{
LoginProvider = table.Column<string>(type: "text", nullable: false),
ProviderKey = table.Column<string>(type: "text", nullable: false),
ProviderDisplayName = table.Column<string>(type: "text", nullable: true),
UserId = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_UserLogins", x => new { x.LoginProvider, x.ProviderKey });
table.ForeignKey(
name: "FK_UserLogins_Users_UserId",
column: x => x.UserId,
principalSchema: "identity",
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "UserPermissions",
schema: "identity",
columns: table => new
{
UserId = table.Column<string>(type: "text", nullable: false),
PermissionId = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_UserPermissions", x => new { x.UserId, x.PermissionId });
table.ForeignKey(
name: "FK_UserPermissions_Permissions_PermissionId",
column: x => x.PermissionId,
principalSchema: "identity",
principalTable: "Permissions",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_UserPermissions_Users_UserId",
column: x => x.UserId,
principalSchema: "identity",
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "UserRoles",
schema: "identity",
columns: table => new
{
UserId = table.Column<string>(type: "text", nullable: false),
RoleId = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_UserRoles", x => new { x.UserId, x.RoleId });
table.ForeignKey(
name: "FK_UserRoles_Roles_RoleId",
column: x => x.RoleId,
principalSchema: "identity",
principalTable: "Roles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_UserRoles_Users_UserId",
column: x => x.UserId,
principalSchema: "identity",
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "UserTokens",
schema: "identity",
columns: table => new
{
UserId = table.Column<string>(type: "text", nullable: false),
LoginProvider = table.Column<string>(type: "text", nullable: false),
Name = table.Column<string>(type: "text", nullable: false),
Value = table.Column<string>(type: "text", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_UserTokens", x => new { x.UserId, x.LoginProvider, x.Name });
table.ForeignKey(
name: "FK_UserTokens_Users_UserId",
column: x => x.UserId,
principalSchema: "identity",
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_AuditLog_UserId",
schema: "system",
table: "AuditLog",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_RoleClaims_RoleId",
schema: "identity",
table: "RoleClaims",
column: "RoleId");
migrationBuilder.CreateIndex(
name: "IX_RolePermissions_PermissionId",
schema: "identity",
table: "RolePermissions",
column: "PermissionId");
migrationBuilder.CreateIndex(
name: "RoleNameIndex",
schema: "identity",
table: "Roles",
column: "NormalizedName",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_UserClaims_UserId",
schema: "identity",
table: "UserClaims",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_UserLogins_UserId",
schema: "identity",
table: "UserLogins",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_UserPermissions_PermissionId",
schema: "identity",
table: "UserPermissions",
column: "PermissionId");
migrationBuilder.CreateIndex(
name: "IX_UserRoles_RoleId",
schema: "identity",
table: "UserRoles",
column: "RoleId");
migrationBuilder.CreateIndex(
name: "EmailIndex",
schema: "identity",
table: "Users",
column: "NormalizedEmail");
migrationBuilder.CreateIndex(
name: "UserNameIndex",
schema: "identity",
table: "Users",
column: "NormalizedUserName",
unique: true);
migrationBuilder.AddForeignKey(
name: "FK_AuditLog_Users_UserId",
schema: "system",
table: "AuditLog",
column: "UserId",
principalSchema: "identity",
principalTable: "Users",
principalColumn: "Id");
}
}
}

View File

@ -1,323 +0,0 @@
// <auto-generated />
using System;
using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Phantom.Controller.Database;
#nullable disable
namespace Phantom.Controller.Database.Postgres.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20231008123315_ReplaceIdentity2")]
partial class ReplaceIdentity2
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "7.0.11")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Phantom.Controller.Database.Entities.AgentEntity", b =>
{
b.Property<Guid>("AgentGuid")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("BuildVersion")
.IsRequired()
.HasColumnType("text");
b.Property<int>("MaxInstances")
.HasColumnType("integer");
b.Property<ushort>("MaxMemory")
.HasColumnType("integer");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<int>("ProtocolVersion")
.HasColumnType("integer");
b.HasKey("AgentGuid");
b.ToTable("Agents", "agents");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.AuditLogEntity", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<JsonDocument>("Data")
.HasColumnType("jsonb");
b.Property<string>("EventType")
.IsRequired()
.HasColumnType("text");
b.Property<string>("SubjectId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("SubjectType")
.IsRequired()
.HasColumnType("text");
b.Property<Guid?>("UserGuid")
.HasColumnType("uuid");
b.Property<DateTime>("UtcTime")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("UserGuid");
b.ToTable("AuditLog", "system");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.EventLogEntity", b =>
{
b.Property<Guid>("EventGuid")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid?>("AgentGuid")
.HasColumnType("uuid");
b.Property<JsonDocument>("Data")
.HasColumnType("jsonb");
b.Property<string>("EventType")
.IsRequired()
.HasColumnType("text");
b.Property<string>("SubjectId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("SubjectType")
.IsRequired()
.HasColumnType("text");
b.Property<DateTime>("UtcTime")
.HasColumnType("timestamp with time zone");
b.HasKey("EventGuid");
b.ToTable("EventLog", "system");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.InstanceEntity", b =>
{
b.Property<Guid>("InstanceGuid")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid>("AgentGuid")
.HasColumnType("uuid");
b.Property<string>("InstanceName")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("JavaRuntimeGuid")
.HasColumnType("uuid");
b.Property<string>("JvmArguments")
.IsRequired()
.HasColumnType("text");
b.Property<bool>("LaunchAutomatically")
.HasColumnType("boolean");
b.Property<ushort>("MemoryAllocation")
.HasColumnType("integer");
b.Property<string>("MinecraftServerKind")
.IsRequired()
.HasColumnType("text");
b.Property<string>("MinecraftVersion")
.IsRequired()
.HasColumnType("text");
b.Property<int>("RconPort")
.HasColumnType("integer");
b.Property<int>("ServerPort")
.HasColumnType("integer");
b.HasKey("InstanceGuid");
b.ToTable("Instances", "agents");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.PermissionEntity", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Permissions", "identity");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.RoleEntity", b =>
{
b.Property<Guid>("RoleGuid")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.HasKey("RoleGuid");
b.ToTable("Roles", "identity");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.RolePermissionEntity", b =>
{
b.Property<Guid>("RoleGuid")
.HasColumnType("uuid");
b.Property<string>("PermissionId")
.HasColumnType("text");
b.HasKey("RoleGuid", "PermissionId");
b.HasIndex("PermissionId");
b.ToTable("RolePermissions", "identity");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.UserEntity", b =>
{
b.Property<Guid>("UserGuid")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("PasswordHash")
.IsRequired()
.HasColumnType("text");
b.HasKey("UserGuid");
b.HasIndex("Name")
.IsUnique();
b.ToTable("Users", "identity");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.UserPermissionEntity", b =>
{
b.Property<Guid>("UserGuid")
.HasColumnType("uuid");
b.Property<string>("PermissionId")
.HasColumnType("text");
b.HasKey("UserGuid", "PermissionId");
b.HasIndex("PermissionId");
b.ToTable("UserPermissions", "identity");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.UserRoleEntity", b =>
{
b.Property<Guid>("UserGuid")
.HasColumnType("uuid");
b.Property<Guid>("RoleGuid")
.HasColumnType("uuid");
b.HasKey("UserGuid", "RoleGuid");
b.HasIndex("RoleGuid");
b.ToTable("UserRoles", "identity");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.AuditLogEntity", b =>
{
b.HasOne("Phantom.Controller.Database.Entities.UserEntity", "User")
.WithMany()
.HasForeignKey("UserGuid")
.OnDelete(DeleteBehavior.SetNull);
b.Navigation("User");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.RolePermissionEntity", b =>
{
b.HasOne("Phantom.Controller.Database.Entities.PermissionEntity", null)
.WithMany()
.HasForeignKey("PermissionId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Phantom.Controller.Database.Entities.RoleEntity", null)
.WithMany()
.HasForeignKey("RoleGuid")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.UserPermissionEntity", b =>
{
b.HasOne("Phantom.Controller.Database.Entities.PermissionEntity", null)
.WithMany()
.HasForeignKey("PermissionId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Phantom.Controller.Database.Entities.UserEntity", null)
.WithMany()
.HasForeignKey("UserGuid")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.UserRoleEntity", b =>
{
b.HasOne("Phantom.Controller.Database.Entities.RoleEntity", "Role")
.WithMany()
.HasForeignKey("RoleGuid")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Phantom.Controller.Database.Entities.UserEntity", "User")
.WithMany()
.HasForeignKey("UserGuid")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Role");
b.Navigation("User");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -1,198 +0,0 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Phantom.Controller.Database.Postgres.Migrations
{
/// <inheritdoc />
public partial class ReplaceIdentity2 : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Roles",
schema: "identity",
columns: table => new
{
RoleGuid = table.Column<Guid>(type: "uuid", nullable: false),
Name = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Roles", x => x.RoleGuid);
});
migrationBuilder.CreateTable(
name: "Users",
schema: "identity",
columns: table => new
{
UserGuid = table.Column<Guid>(type: "uuid", nullable: false),
Name = table.Column<string>(type: "text", nullable: false),
PasswordHash = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Users", x => x.UserGuid);
});
migrationBuilder.CreateTable(
name: "RolePermissions",
schema: "identity",
columns: table => new
{
RoleGuid = table.Column<Guid>(type: "uuid", nullable: false),
PermissionId = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_RolePermissions", x => new { x.RoleGuid, x.PermissionId });
table.ForeignKey(
name: "FK_RolePermissions_Permissions_PermissionId",
column: x => x.PermissionId,
principalSchema: "identity",
principalTable: "Permissions",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_RolePermissions_Roles_RoleGuid",
column: x => x.RoleGuid,
principalSchema: "identity",
principalTable: "Roles",
principalColumn: "RoleGuid",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "UserPermissions",
schema: "identity",
columns: table => new
{
UserGuid = table.Column<Guid>(type: "uuid", nullable: false),
PermissionId = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_UserPermissions", x => new { x.UserGuid, x.PermissionId });
table.ForeignKey(
name: "FK_UserPermissions_Permissions_PermissionId",
column: x => x.PermissionId,
principalSchema: "identity",
principalTable: "Permissions",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_UserPermissions_Users_UserGuid",
column: x => x.UserGuid,
principalSchema: "identity",
principalTable: "Users",
principalColumn: "UserGuid",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "UserRoles",
schema: "identity",
columns: table => new
{
UserGuid = table.Column<Guid>(type: "uuid", nullable: false),
RoleGuid = table.Column<Guid>(type: "uuid", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_UserRoles", x => new { x.UserGuid, x.RoleGuid });
table.ForeignKey(
name: "FK_UserRoles_Roles_RoleGuid",
column: x => x.RoleGuid,
principalSchema: "identity",
principalTable: "Roles",
principalColumn: "RoleGuid",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_UserRoles_Users_UserGuid",
column: x => x.UserGuid,
principalSchema: "identity",
principalTable: "Users",
principalColumn: "UserGuid",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_AuditLog_UserGuid",
schema: "system",
table: "AuditLog",
column: "UserGuid");
migrationBuilder.CreateIndex(
name: "IX_RolePermissions_PermissionId",
schema: "identity",
table: "RolePermissions",
column: "PermissionId");
migrationBuilder.CreateIndex(
name: "IX_UserPermissions_PermissionId",
schema: "identity",
table: "UserPermissions",
column: "PermissionId");
migrationBuilder.CreateIndex(
name: "IX_UserRoles_RoleGuid",
schema: "identity",
table: "UserRoles",
column: "RoleGuid");
migrationBuilder.CreateIndex(
name: "IX_Users_Name",
schema: "identity",
table: "Users",
column: "Name",
unique: true);
migrationBuilder.AddForeignKey(
name: "FK_AuditLog_Users_UserGuid",
schema: "system",
table: "AuditLog",
column: "UserGuid",
principalSchema: "identity",
principalTable: "Users",
principalColumn: "UserGuid",
onDelete: ReferentialAction.SetNull);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_AuditLog_Users_UserGuid",
schema: "system",
table: "AuditLog");
migrationBuilder.DropTable(
name: "RolePermissions",
schema: "identity");
migrationBuilder.DropTable(
name: "UserPermissions",
schema: "identity");
migrationBuilder.DropTable(
name: "UserRoles",
schema: "identity");
migrationBuilder.DropTable(
name: "Roles",
schema: "identity");
migrationBuilder.DropTable(
name: "Users",
schema: "identity");
migrationBuilder.DropIndex(
name: "IX_AuditLog_UserGuid",
schema: "system",
table: "AuditLog");
}
}
}

View File

@ -1,320 +0,0 @@
// <auto-generated />
using System;
using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Phantom.Controller.Database;
#nullable disable
namespace Phantom.Controller.Database.Postgres.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
partial class ApplicationDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "7.0.11")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Phantom.Controller.Database.Entities.AgentEntity", b =>
{
b.Property<Guid>("AgentGuid")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("BuildVersion")
.IsRequired()
.HasColumnType("text");
b.Property<int>("MaxInstances")
.HasColumnType("integer");
b.Property<ushort>("MaxMemory")
.HasColumnType("integer");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<int>("ProtocolVersion")
.HasColumnType("integer");
b.HasKey("AgentGuid");
b.ToTable("Agents", "agents");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.AuditLogEntity", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<JsonDocument>("Data")
.HasColumnType("jsonb");
b.Property<string>("EventType")
.IsRequired()
.HasColumnType("text");
b.Property<string>("SubjectId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("SubjectType")
.IsRequired()
.HasColumnType("text");
b.Property<Guid?>("UserGuid")
.HasColumnType("uuid");
b.Property<DateTime>("UtcTime")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("UserGuid");
b.ToTable("AuditLog", "system");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.EventLogEntity", b =>
{
b.Property<Guid>("EventGuid")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid?>("AgentGuid")
.HasColumnType("uuid");
b.Property<JsonDocument>("Data")
.HasColumnType("jsonb");
b.Property<string>("EventType")
.IsRequired()
.HasColumnType("text");
b.Property<string>("SubjectId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("SubjectType")
.IsRequired()
.HasColumnType("text");
b.Property<DateTime>("UtcTime")
.HasColumnType("timestamp with time zone");
b.HasKey("EventGuid");
b.ToTable("EventLog", "system");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.InstanceEntity", b =>
{
b.Property<Guid>("InstanceGuid")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid>("AgentGuid")
.HasColumnType("uuid");
b.Property<string>("InstanceName")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("JavaRuntimeGuid")
.HasColumnType("uuid");
b.Property<string>("JvmArguments")
.IsRequired()
.HasColumnType("text");
b.Property<bool>("LaunchAutomatically")
.HasColumnType("boolean");
b.Property<ushort>("MemoryAllocation")
.HasColumnType("integer");
b.Property<string>("MinecraftServerKind")
.IsRequired()
.HasColumnType("text");
b.Property<string>("MinecraftVersion")
.IsRequired()
.HasColumnType("text");
b.Property<int>("RconPort")
.HasColumnType("integer");
b.Property<int>("ServerPort")
.HasColumnType("integer");
b.HasKey("InstanceGuid");
b.ToTable("Instances", "agents");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.PermissionEntity", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Permissions", "identity");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.RoleEntity", b =>
{
b.Property<Guid>("RoleGuid")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.HasKey("RoleGuid");
b.ToTable("Roles", "identity");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.RolePermissionEntity", b =>
{
b.Property<Guid>("RoleGuid")
.HasColumnType("uuid");
b.Property<string>("PermissionId")
.HasColumnType("text");
b.HasKey("RoleGuid", "PermissionId");
b.HasIndex("PermissionId");
b.ToTable("RolePermissions", "identity");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.UserEntity", b =>
{
b.Property<Guid>("UserGuid")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("PasswordHash")
.IsRequired()
.HasColumnType("text");
b.HasKey("UserGuid");
b.HasIndex("Name")
.IsUnique();
b.ToTable("Users", "identity");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.UserPermissionEntity", b =>
{
b.Property<Guid>("UserGuid")
.HasColumnType("uuid");
b.Property<string>("PermissionId")
.HasColumnType("text");
b.HasKey("UserGuid", "PermissionId");
b.HasIndex("PermissionId");
b.ToTable("UserPermissions", "identity");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.UserRoleEntity", b =>
{
b.Property<Guid>("UserGuid")
.HasColumnType("uuid");
b.Property<Guid>("RoleGuid")
.HasColumnType("uuid");
b.HasKey("UserGuid", "RoleGuid");
b.HasIndex("RoleGuid");
b.ToTable("UserRoles", "identity");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.AuditLogEntity", b =>
{
b.HasOne("Phantom.Controller.Database.Entities.UserEntity", "User")
.WithMany()
.HasForeignKey("UserGuid")
.OnDelete(DeleteBehavior.SetNull);
b.Navigation("User");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.RolePermissionEntity", b =>
{
b.HasOne("Phantom.Controller.Database.Entities.PermissionEntity", null)
.WithMany()
.HasForeignKey("PermissionId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Phantom.Controller.Database.Entities.RoleEntity", null)
.WithMany()
.HasForeignKey("RoleGuid")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.UserPermissionEntity", b =>
{
b.HasOne("Phantom.Controller.Database.Entities.PermissionEntity", null)
.WithMany()
.HasForeignKey("PermissionId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Phantom.Controller.Database.Entities.UserEntity", null)
.WithMany()
.HasForeignKey("UserGuid")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.UserRoleEntity", b =>
{
b.HasOne("Phantom.Controller.Database.Entities.RoleEntity", "Role")
.WithMany()
.HasForeignKey("RoleGuid")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Phantom.Controller.Database.Entities.UserEntity", "User")
.WithMany()
.HasForeignKey("UserGuid")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Role");
b.Navigation("User");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -1,17 +0,0 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Phantom.Controller.Database.Entities;
[Table("Roles", Schema = "identity")]
public sealed class RoleEntity {
[Key]
public Guid RoleGuid { get; set; }
public string Name { get; set; }
public RoleEntity(Guid roleGuid, string name) {
RoleGuid = roleGuid;
Name = name;
}
}

View File

@ -1,19 +0,0 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Phantom.Controller.Database.Entities;
[Table("Users", Schema = "identity")]
public sealed class UserEntity {
[Key]
public Guid UserGuid { get; set; }
public string Name { get; set; }
public string PasswordHash { get; set; }
public UserEntity(Guid userGuid, string name) {
UserGuid = userGuid;
Name = name;
PasswordHash = null!;
}
}

View File

@ -1,19 +0,0 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace Phantom.Controller.Database.Entities;
[Table("UserRoles", Schema = "identity")]
public sealed class UserRoleEntity {
public Guid UserGuid { get; set; }
public Guid RoleGuid { get; set; }
public UserEntity User { get; set; }
public RoleEntity Role { get; set; }
public UserRoleEntity(Guid userGuid, Guid roleGuid) {
UserGuid = userGuid;
RoleGuid = roleGuid;
User = null!;
Role = null!;
}
}

View File

@ -1,5 +0,0 @@
namespace Phantom.Controller.Database.Enums;
public enum EventLogSubjectType {
Instance
}

View File

@ -1,6 +0,0 @@
using System.Text.Json;
using Phantom.Controller.Database.Enums;
namespace Phantom.Controller.Services.Audit;
public sealed record AuditLogItem(DateTime UtcTime, Guid? UserGuid, string? UserName, AuditLogEventType EventType, AuditLogSubjectType SubjectType, string? SubjectId, JsonDocument? Data);

View File

@ -1,8 +0,0 @@
namespace Phantom.Controller.Services.Users;
public enum AddRoleError : byte {
NameIsEmpty,
NameIsTooLong,
NameAlreadyExists,
UnknownError
}

View File

@ -1,29 +0,0 @@
using System.Collections.Immutable;
namespace Phantom.Controller.Services.Users;
public abstract record AddUserError {
private AddUserError() {}
public sealed record NameIsEmpty : AddUserError;
public sealed record NameIsTooLong(int MaximumLength) : AddUserError;
public sealed record NameAlreadyExists : AddUserError;
public sealed record PasswordIsInvalid(ImmutableArray<PasswordRequirementViolation> Violations) : AddUserError;
public sealed record UnknownError : AddUserError;
}
public static class AddUserErrorExtensions {
public static string ToSentences(this AddUserError error, string delimiter) {
return error switch {
AddUserError.NameIsEmpty => "Name cannot be empty.",
AddUserError.NameIsTooLong e => "Name cannot be longer than " + e.MaximumLength + " character(s).",
AddUserError.NameAlreadyExists => "Name is already occupied.",
AddUserError.PasswordIsInvalid e => string.Join(delimiter, e.Violations.Select(static v => v.ToSentence())),
_ => "Unknown error."
};
}
}

View File

@ -1,7 +0,0 @@
namespace Phantom.Controller.Services.Users;
public enum DeleteUserResult : byte {
Deleted,
NotFound,
Failed
}

View File

@ -1,25 +0,0 @@
namespace Phantom.Controller.Services.Users;
public abstract record PasswordRequirementViolation {
private PasswordRequirementViolation() {}
public sealed record TooShort(int MinimumLength) : PasswordRequirementViolation;
public sealed record LowercaseLetterRequired : PasswordRequirementViolation;
public sealed record UppercaseLetterRequired : PasswordRequirementViolation;
public sealed record DigitRequired : PasswordRequirementViolation;
}
public static class PasswordRequirementViolationExtensions {
public static string ToSentence(this PasswordRequirementViolation violation) {
return violation switch {
PasswordRequirementViolation.TooShort v => "Password must be at least " + v.MinimumLength + " character(s) long.",
PasswordRequirementViolation.LowercaseLetterRequired => "Password must contain a lowercase letter.",
PasswordRequirementViolation.UppercaseLetterRequired => "Password must contain an uppercase letter.",
PasswordRequirementViolation.DigitRequired => "Password must contain a digit.",
_ => "Unknown error."
};
}
}

View File

@ -1,60 +0,0 @@
using System.Collections.Immutable;
using Microsoft.EntityFrameworkCore;
using Phantom.Common.Logging;
using Phantom.Controller.Database;
using Phantom.Controller.Database.Entities;
using Phantom.Utils.Collections;
using Phantom.Utils.Tasks;
using ILogger = Serilog.ILogger;
namespace Phantom.Controller.Services.Users;
public sealed class RoleManager {
private static readonly ILogger Logger = PhantomLogger.Create<RoleManager>();
private const int MaxRoleNameLength = 40;
private readonly ApplicationDbContext db;
public RoleManager(ApplicationDbContext db) {
this.db = db;
}
public Task<List<RoleEntity>> GetAll() {
return db.Roles.ToListAsync();
}
public Task<ImmutableHashSet<string>> GetAllNames() {
return db.Roles.Select(static role => role.Name).AsAsyncEnumerable().ToImmutableSetAsync();
}
public ValueTask<RoleEntity?> GetByGuid(Guid guid) {
return db.Roles.FindAsync(guid);
}
public async Task<Result<RoleEntity, AddRoleError>> Create(Guid guid, string name) {
if (string.IsNullOrWhiteSpace(name)) {
return Result.Fail<RoleEntity, AddRoleError>(AddRoleError.NameIsEmpty);
}
else if (name.Length > MaxRoleNameLength) {
return Result.Fail<RoleEntity, AddRoleError>(AddRoleError.NameIsTooLong);
}
try {
if (await db.Roles.AnyAsync(role => role.Name == name)) {
return Result.Fail<RoleEntity, AddRoleError>(AddRoleError.NameAlreadyExists);
}
var role = new RoleEntity(guid, name);
db.Roles.Add(role);
await db.SaveChangesAsync();
Logger.Information("Created role \"{Name}\" (GUID {Guid}).", name, guid);
return Result.Ok<RoleEntity, AddRoleError>(role);
} catch (Exception e) {
Logger.Error(e, "Could not create role \"{Name}\" (GUID {Guid}).", name, guid);
return Result.Fail<RoleEntity, AddRoleError>(AddRoleError.UnknownError);
}
}
}

View File

@ -1,23 +0,0 @@
using System.Collections.Immutable;
namespace Phantom.Controller.Services.Users;
public abstract record SetUserPasswordError {
private SetUserPasswordError() {}
public sealed record UserNotFound : SetUserPasswordError;
public sealed record PasswordIsInvalid(ImmutableArray<PasswordRequirementViolation> Violations) : SetUserPasswordError;
public sealed record UnknownError : SetUserPasswordError;
}
public static class SetUserPasswordErrorExtensions {
public static string ToSentences(this SetUserPasswordError error, string delimiter) {
return error switch {
SetUserPasswordError.UserNotFound => "User not found.",
SetUserPasswordError.PasswordIsInvalid e => string.Join(delimiter, e.Violations.Select(static v => v.ToSentence())),
_ => "Unknown error."
};
}
}

View File

@ -1,148 +0,0 @@
using System.Collections.Immutable;
using System.Security.Claims;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Phantom.Common.Logging;
using Phantom.Controller.Database;
using Phantom.Controller.Database.Entities;
using Phantom.Utils.Collections;
using Phantom.Utils.Tasks;
using ILogger = Serilog.ILogger;
namespace Phantom.Controller.Services.Users;
public sealed class UserManager {
private static readonly ILogger Logger = PhantomLogger.Create<UserManager>();
private const int MaxUserNameLength = 40;
private readonly ApplicationDbContext db;
public UserManager(ApplicationDbContext db) {
this.db = db;
}
public static Guid? GetAuthenticatedUserId(ClaimsPrincipal user) {
if (user.Identity is not { IsAuthenticated: true }) {
return null;
}
var claim = user.FindFirst(ClaimTypes.NameIdentifier);
if (claim == null) {
return null;
}
return Guid.TryParse(claim.Value, out var guid) ? guid : null;
}
public Task<ImmutableArray<UserEntity>> GetAll() {
return db.Users.AsAsyncEnumerable().ToImmutableArrayAsync();
}
public Task<Dictionary<Guid, T>> GetAllByGuid<T>(Func<UserEntity, T> valueSelector, CancellationToken cancellationToken = default) {
return db.Users.ToDictionaryAsync(static user => user.UserGuid, valueSelector, cancellationToken);
}
public Task<UserEntity?> GetByName(string username) {
return db.Users.FirstOrDefaultAsync(user => user.Name == username);
}
public async Task<UserEntity?> GetAuthenticated(string username, string password) {
var user = await db.Users.FirstOrDefaultAsync(user => user.Name == username);
if (user == null) {
return null;
}
switch (UserPasswords.Verify(user, password)) {
case PasswordVerificationResult.SuccessRehashNeeded:
try {
UserPasswords.Set(user, password);
await db.SaveChangesAsync();
} catch (Exception e) {
Logger.Warning(e, "Could not rehash password for \"{Username}\".", user.Name);
}
goto case PasswordVerificationResult.Success;
case PasswordVerificationResult.Success:
return user;
case PasswordVerificationResult.Failed:
return null;
}
throw new InvalidOperationException();
}
public async Task<Result<UserEntity, AddUserError>> CreateUser(string username, string password) {
if (string.IsNullOrWhiteSpace(username)) {
return Result.Fail<UserEntity, AddUserError>(new AddUserError.NameIsEmpty());
}
else if (username.Length > MaxUserNameLength) {
return Result.Fail<UserEntity, AddUserError>(new AddUserError.NameIsTooLong(MaxUserNameLength));
}
var requirementViolations = UserPasswords.CheckRequirements(password);
if (!requirementViolations.IsEmpty) {
return Result.Fail<UserEntity, AddUserError>(new AddUserError.PasswordIsInvalid(requirementViolations));
}
try {
if (await db.Users.AnyAsync(user => user.Name == username)) {
return Result.Fail<UserEntity, AddUserError>(new AddUserError.NameAlreadyExists());
}
var guid = Guid.NewGuid();
var user = new UserEntity(guid, username);
UserPasswords.Set(user, password);
db.Users.Add(user);
await db.SaveChangesAsync();
Logger.Information("Created user \"{Name}\" (GUID {Guid}).", username, guid);
return Result.Ok<UserEntity, AddUserError>(user);
} catch (Exception e) {
Logger.Error(e, "Could not create user \"{Name}\".", username);
return Result.Fail<UserEntity, AddUserError>(new AddUserError.UnknownError());
}
}
public async Task<Result<SetUserPasswordError>> SetUserPassword(Guid guid, string password) {
var user = await db.Users.FindAsync(guid);
if (user == null) {
return Result.Fail<SetUserPasswordError>(new SetUserPasswordError.UserNotFound());
}
try {
var requirementViolations = UserPasswords.CheckRequirements(password);
if (!requirementViolations.IsEmpty) {
return Result.Fail<SetUserPasswordError>(new SetUserPasswordError.PasswordIsInvalid(requirementViolations));
}
UserPasswords.Set(user, password);
await db.SaveChangesAsync();
Logger.Information("Changed password for user \"{Name}\" (GUID {Guid}).", user.Name, user.UserGuid);
return Result.Ok<SetUserPasswordError>();
} catch (Exception e) {
Logger.Error(e, "Could not change password for user \"{Name}\" (GUID {Guid}).", user.Name, user.UserGuid);
return Result.Fail<SetUserPasswordError>(new SetUserPasswordError.UnknownError());
}
}
public async Task<DeleteUserResult> DeleteByGuid(Guid guid) {
var user = await db.Users.FindAsync(guid);
if (user == null) {
return DeleteUserResult.NotFound;
}
try {
db.Users.Remove(user);
await db.SaveChangesAsync();
return DeleteUserResult.Deleted;
} catch (Exception e) {
Logger.Error(e, "Could not delete user \"{Name}\" (GUID {Guid}).", user.Name, user.UserGuid);
return DeleteUserResult.Failed;
}
}
}

View File

@ -1,41 +0,0 @@
using System.Collections.Immutable;
using Microsoft.AspNetCore.Identity;
using Phantom.Controller.Database.Entities;
namespace Phantom.Controller.Services.Users;
internal static class UserPasswords {
private static PasswordHasher<UserEntity> Hasher { get; } = new ();
private const int MinimumLength = 16;
public static ImmutableArray<PasswordRequirementViolation> CheckRequirements(string password) {
var violations = ImmutableArray.CreateBuilder<PasswordRequirementViolation>();
if (password.Length < MinimumLength) {
violations.Add(new PasswordRequirementViolation.TooShort(MinimumLength));
}
if (!password.Any(char.IsLower)) {
violations.Add(new PasswordRequirementViolation.LowercaseLetterRequired());
}
if (!password.Any(char.IsUpper)) {
violations.Add(new PasswordRequirementViolation.UppercaseLetterRequired());
}
if (!password.Any(char.IsDigit)) {
violations.Add(new PasswordRequirementViolation.DigitRequired());
}
return violations.ToImmutable();
}
public static void Set(UserEntity user, string password) {
user.PasswordHash = Hasher.HashPassword(user, password);
}
public static PasswordVerificationResult Verify(UserEntity user, string password) {
return Hasher.VerifyHashedPassword(user, user.PasswordHash, password);
}
}

View File

@ -1,76 +0,0 @@
using System.Collections.Immutable;
using Microsoft.EntityFrameworkCore;
using Phantom.Common.Logging;
using Phantom.Controller.Database;
using Phantom.Controller.Database.Entities;
using Phantom.Utils.Collections;
using ILogger = Serilog.ILogger;
namespace Phantom.Controller.Services.Users;
public sealed class UserRoleManager {
private static readonly ILogger Logger = PhantomLogger.Create<UserRoleManager>();
private readonly ApplicationDbContext db;
public UserRoleManager(ApplicationDbContext db) {
this.db = db;
}
public Task<Dictionary<Guid, ImmutableArray<RoleEntity>>> GetAllByUserGuid() {
return db.UserRoles
.Include(static ur => ur.Role)
.GroupBy(static ur => ur.UserGuid, static ur => ur.Role)
.ToDictionaryAsync(static group => group.Key, static group => group.ToImmutableArray());
}
public Task<ImmutableArray<RoleEntity>> GetUserRoles(UserEntity user) {
return db.UserRoles
.Include(static ur => ur.Role)
.Where(ur => ur.UserGuid == user.UserGuid)
.Select(static ur => ur.Role)
.AsAsyncEnumerable()
.ToImmutableArrayAsync();
}
public Task<ImmutableHashSet<Guid>> GetUserRoleGuids(UserEntity user) {
return db.UserRoles
.Where(ur => ur.UserGuid == user.UserGuid)
.Select(static ur => ur.RoleGuid)
.AsAsyncEnumerable()
.ToImmutableSetAsync();
}
public async Task<bool> Add(UserEntity user, RoleEntity role) {
try {
var userRole = await db.UserRoles.FindAsync(user.UserGuid, role.RoleGuid);
if (userRole == null) {
userRole = new UserRoleEntity(user.UserGuid, role.RoleGuid);
db.UserRoles.Add(userRole);
await db.SaveChangesAsync();
}
Logger.Information("Added user \"{UserName}\" (GUID {UserGuid}) to role \"{RoleName}\" (GUID {RoleGuid}).", user.Name, user.UserGuid, role.Name, role.RoleGuid);
return true;
} catch (Exception e) {
Logger.Error(e, "Could not add user \"{UserName}\" (GUID {UserGuid}) to role \"{RoleName}\" (GUID {RoleGuid}).", user.Name, user.UserGuid, role.Name, role.RoleGuid);
return false;
}
}
public async Task<bool> Remove(UserEntity user, RoleEntity role) {
try {
var userRole = await db.UserRoles.FindAsync(user.UserGuid, role.RoleGuid);
if (userRole != null) {
db.UserRoles.Remove(userRole);
await db.SaveChangesAsync();
}
Logger.Information("Removed user \"{UserName}\" (GUID {UserGuid}) from role \"{RoleName}\" (GUID {RoleGuid}).", user.Name, user.UserGuid, role.Name, role.RoleGuid);
return true;
} catch (Exception e) {
Logger.Error(e, "Could not remove user \"{UserName}\" (GUID {UserGuid}) from role \"{RoleName}\" (GUID {RoleGuid}).", user.Name, user.UserGuid, role.Name, role.RoleGuid);
return false;
}
}
}

View File

@ -29,7 +29,7 @@ RUN dotnet publish Agent/Phantom.Agent/Phantom.Agent.csproj \
# +----------------------+
FROM phantom-base-builder AS phantom-server-builder
RUN dotnet publish Web/Phantom.Web/Phantom.Web.csproj \
RUN dotnet publish Server/Phantom.Server.Web/Phantom.Server.Web.csproj \
/p:DebugType=None \
/p:DebugSymbols=false \
--no-restore \
@ -37,7 +37,7 @@ RUN dotnet publish Web/Phantom.Web/Phantom.Web.csproj \
--configuration Release \
--output /app/out
RUN dotnet publish Controller/Phantom.Controller/Phantom.Controller.csproj \
RUN dotnet publish Server/Phantom.Server/Phantom.Server.csproj \
/p:DebugType=None \
/p:DebugSymbols=false \
--no-restore \
@ -83,4 +83,4 @@ WORKDIR /data
COPY --from=phantom-server-builder --chmod=755 /app/out /app
ENTRYPOINT ["dotnet", "/app/Phantom.Controller.dll"]
ENTRYPOINT ["dotnet", "/app/Phantom.Server.dll"]

View File

@ -3,7 +3,7 @@
<ItemGroup>
<PackageReference Update="Microsoft.AspNetCore.Components.Authorization" Version="7.0.11" />
<PackageReference Update="Microsoft.AspNetCore.Components.Web" Version="7.0.11" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Relational" Version="7.0.11" />
<PackageReference Update="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="7.0.11" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Tools" Version="7.0.11" />
<PackageReference Update="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.11" />
</ItemGroup>

View File

@ -6,14 +6,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Common", "Common", "{01CB1A
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Common.Tests", "Common.Tests", "{D781E00D-8563-4102-A0CD-477A679193B5}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Controller", "Controller", "{0AB9471E-6228-4EB7-802E-3102B3952AAD}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Server", "Server", "{8AC8FB6C-033A-4626-820F-ED0F908756B2}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Utils", "Utils", "{AA217EB8-E480-456B-BDF3-39419EF2AD85}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Utils.Tests", "Utils.Tests", "{7A3C7B26-26A0-49B9-8505-5F8267C10F10}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Web", "Web", "{92B26F48-235F-4500-BD55-800F06A0BA39}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Phantom.Agent", "Agent\Phantom.Agent\Phantom.Agent.csproj", "{418BE1BF-9F63-4B46-B4E4-DF64C3B3DDA7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Phantom.Agent.Minecraft", "Agent\Phantom.Agent.Minecraft\Phantom.Agent.Minecraft.csproj", "{9FE000D0-91AC-4CB4-8956-91CCC0270015}"
@ -30,17 +28,25 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Phantom.Common.Logging", "C
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Phantom.Common.Messages", "Common\Phantom.Common.Messages\Phantom.Common.Messages.csproj", "{95B55357-F8F0-48C2-A1C2-5EA997651783}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Phantom.Controller", "Controller\Phantom.Controller\Phantom.Controller.csproj", "{A0F1C595-96B6-4DBF-8C16-6B99223F8F35}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Phantom.Server", "Server\Phantom.Server\Phantom.Server.csproj", "{A0F1C595-96B6-4DBF-8C16-6B99223F8F35}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Phantom.Controller.Database", "Controller\Phantom.Controller.Database\Phantom.Controller.Database.csproj", "{E3AD566F-384A-489A-A3BB-EA3BA400C18C}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Phantom.Server.Database", "Server\Phantom.Server.Database\Phantom.Server.Database.csproj", "{E3AD566F-384A-489A-A3BB-EA3BA400C18C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Phantom.Controller.Database.Postgres", "Controller\Phantom.Controller.Database.Postgres\Phantom.Controller.Database.Postgres.csproj", "{81625B4A-3DB6-48BD-A739-D23DA02107D1}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Phantom.Server.Database.Postgres", "Server\Phantom.Server.Database.Postgres\Phantom.Server.Database.Postgres.csproj", "{81625B4A-3DB6-48BD-A739-D23DA02107D1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Phantom.Controller.Minecraft", "Controller\Phantom.Controller.Minecraft\Phantom.Controller.Minecraft.csproj", "{4B3B73E6-48DD-4846-87FD-DFB86619B67C}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Phantom.Server.Minecraft", "Server\Phantom.Server.Minecraft\Phantom.Server.Minecraft.csproj", "{4B3B73E6-48DD-4846-87FD-DFB86619B67C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Phantom.Controller.Rpc", "Controller\Phantom.Controller.Rpc\Phantom.Controller.Rpc.csproj", "{79312D72-44E0-431D-96A4-4C0A066B9671}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Phantom.Server.Rpc", "Server\Phantom.Server.Rpc\Phantom.Server.Rpc.csproj", "{79312D72-44E0-431D-96A4-4C0A066B9671}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Phantom.Controller.Services", "Controller\Phantom.Controller.Services\Phantom.Controller.Services.csproj", "{90F0F1B1-EB0A-49C9-8DF0-1153A87F77C9}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Phantom.Server.Services", "Server\Phantom.Server.Services\Phantom.Server.Services.csproj", "{90F0F1B1-EB0A-49C9-8DF0-1153A87F77C9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Phantom.Server.Web", "Server\Phantom.Server.Web\Phantom.Server.Web.csproj", "{7CA2E5FE-E507-4DC6-930C-E18711A9F856}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Phantom.Server.Web.Bootstrap", "Server\Phantom.Server.Web.Bootstrap\Phantom.Server.Web.Bootstrap.proj", "{83FA86DB-34E4-4C2C-832C-90F491CA10C7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Phantom.Server.Web.Components", "Server\Phantom.Server.Web.Components\Phantom.Server.Web.Components.csproj", "{3F4F9059-F869-42D3-B92C-90D27ADFC42D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Phantom.Server.Web.Identity", "Server\Phantom.Server.Web.Identity\Phantom.Server.Web.Identity.csproj", "{A9870842-FE7A-4760-95DC-9D485DDDA31F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Phantom.Utils", "Utils\Phantom.Utils\Phantom.Utils.csproj", "{384885E2-5113-45C5-9B15-09BDA0911852}"
EndProject
@ -50,14 +56,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Phantom.Utils.Rpc", "Utils\
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Phantom.Utils.Tests", "Utils\Phantom.Utils.Tests\Phantom.Utils.Tests.csproj", "{742599E6-2FC2-4B39-85B8-976C98013030}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Phantom.Web", "Web\Phantom.Web\Phantom.Web.csproj", "{7CA2E5FE-E507-4DC6-930C-E18711A9F856}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Phantom.Web.Bootstrap", "Web\Phantom.Web.Bootstrap\Phantom.Web.Bootstrap.proj", "{83FA86DB-34E4-4C2C-832C-90F491CA10C7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Phantom.Web.Components", "Web\Phantom.Web.Components\Phantom.Web.Components.csproj", "{3F4F9059-F869-42D3-B92C-90D27ADFC42D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Phantom.Web.Identity", "Web\Phantom.Web.Identity\Phantom.Web.Identity.csproj", "{A9870842-FE7A-4760-95DC-9D485DDDA31F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -120,6 +118,18 @@ Global
{90F0F1B1-EB0A-49C9-8DF0-1153A87F77C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{90F0F1B1-EB0A-49C9-8DF0-1153A87F77C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{90F0F1B1-EB0A-49C9-8DF0-1153A87F77C9}.Release|Any CPU.Build.0 = Release|Any CPU
{7CA2E5FE-E507-4DC6-930C-E18711A9F856}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7CA2E5FE-E507-4DC6-930C-E18711A9F856}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7CA2E5FE-E507-4DC6-930C-E18711A9F856}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7CA2E5FE-E507-4DC6-930C-E18711A9F856}.Release|Any CPU.Build.0 = Release|Any CPU
{3F4F9059-F869-42D3-B92C-90D27ADFC42D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3F4F9059-F869-42D3-B92C-90D27ADFC42D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3F4F9059-F869-42D3-B92C-90D27ADFC42D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3F4F9059-F869-42D3-B92C-90D27ADFC42D}.Release|Any CPU.Build.0 = Release|Any CPU
{A9870842-FE7A-4760-95DC-9D485DDDA31F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A9870842-FE7A-4760-95DC-9D485DDDA31F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A9870842-FE7A-4760-95DC-9D485DDDA31F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A9870842-FE7A-4760-95DC-9D485DDDA31F}.Release|Any CPU.Build.0 = Release|Any CPU
{384885E2-5113-45C5-9B15-09BDA0911852}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{384885E2-5113-45C5-9B15-09BDA0911852}.Debug|Any CPU.Build.0 = Debug|Any CPU
{384885E2-5113-45C5-9B15-09BDA0911852}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -136,18 +146,6 @@ Global
{742599E6-2FC2-4B39-85B8-976C98013030}.Debug|Any CPU.Build.0 = Debug|Any CPU
{742599E6-2FC2-4B39-85B8-976C98013030}.Release|Any CPU.ActiveCfg = Release|Any CPU
{742599E6-2FC2-4B39-85B8-976C98013030}.Release|Any CPU.Build.0 = Release|Any CPU
{7CA2E5FE-E507-4DC6-930C-E18711A9F856}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7CA2E5FE-E507-4DC6-930C-E18711A9F856}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7CA2E5FE-E507-4DC6-930C-E18711A9F856}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7CA2E5FE-E507-4DC6-930C-E18711A9F856}.Release|Any CPU.Build.0 = Release|Any CPU
{3F4F9059-F869-42D3-B92C-90D27ADFC42D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3F4F9059-F869-42D3-B92C-90D27ADFC42D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3F4F9059-F869-42D3-B92C-90D27ADFC42D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3F4F9059-F869-42D3-B92C-90D27ADFC42D}.Release|Any CPU.Build.0 = Release|Any CPU
{A9870842-FE7A-4760-95DC-9D485DDDA31F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A9870842-FE7A-4760-95DC-9D485DDDA31F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A9870842-FE7A-4760-95DC-9D485DDDA31F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A9870842-FE7A-4760-95DC-9D485DDDA31F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{418BE1BF-9F63-4B46-B4E4-DF64C3B3DDA7} = {F5878792-64C8-4ECF-A075-66341FF97127}
@ -158,19 +156,19 @@ Global
{D7F55010-B3ED-42A5-8D83-E754FFC5F2A2} = {01CB1A81-8950-471C-BFDF-F135FDDB2C18}
{95B55357-F8F0-48C2-A1C2-5EA997651783} = {01CB1A81-8950-471C-BFDF-F135FDDB2C18}
{435D7981-DFDA-46A0-8CD8-CD8C117935D7} = {D781E00D-8563-4102-A0CD-477A679193B5}
{A0F1C595-96B6-4DBF-8C16-6B99223F8F35} = {0AB9471E-6228-4EB7-802E-3102B3952AAD}
{E3AD566F-384A-489A-A3BB-EA3BA400C18C} = {0AB9471E-6228-4EB7-802E-3102B3952AAD}
{81625B4A-3DB6-48BD-A739-D23DA02107D1} = {0AB9471E-6228-4EB7-802E-3102B3952AAD}
{4B3B73E6-48DD-4846-87FD-DFB86619B67C} = {0AB9471E-6228-4EB7-802E-3102B3952AAD}
{79312D72-44E0-431D-96A4-4C0A066B9671} = {0AB9471E-6228-4EB7-802E-3102B3952AAD}
{90F0F1B1-EB0A-49C9-8DF0-1153A87F77C9} = {0AB9471E-6228-4EB7-802E-3102B3952AAD}
{A0F1C595-96B6-4DBF-8C16-6B99223F8F35} = {8AC8FB6C-033A-4626-820F-ED0F908756B2}
{E3AD566F-384A-489A-A3BB-EA3BA400C18C} = {8AC8FB6C-033A-4626-820F-ED0F908756B2}
{81625B4A-3DB6-48BD-A739-D23DA02107D1} = {8AC8FB6C-033A-4626-820F-ED0F908756B2}
{4B3B73E6-48DD-4846-87FD-DFB86619B67C} = {8AC8FB6C-033A-4626-820F-ED0F908756B2}
{79312D72-44E0-431D-96A4-4C0A066B9671} = {8AC8FB6C-033A-4626-820F-ED0F908756B2}
{90F0F1B1-EB0A-49C9-8DF0-1153A87F77C9} = {8AC8FB6C-033A-4626-820F-ED0F908756B2}
{7CA2E5FE-E507-4DC6-930C-E18711A9F856} = {8AC8FB6C-033A-4626-820F-ED0F908756B2}
{83FA86DB-34E4-4C2C-832C-90F491CA10C7} = {8AC8FB6C-033A-4626-820F-ED0F908756B2}
{3F4F9059-F869-42D3-B92C-90D27ADFC42D} = {8AC8FB6C-033A-4626-820F-ED0F908756B2}
{A9870842-FE7A-4760-95DC-9D485DDDA31F} = {8AC8FB6C-033A-4626-820F-ED0F908756B2}
{384885E2-5113-45C5-9B15-09BDA0911852} = {AA217EB8-E480-456B-BDF3-39419EF2AD85}
{2E81523B-5DBE-4992-A77B-1679758D0688} = {AA217EB8-E480-456B-BDF3-39419EF2AD85}
{BB112660-7A20-45E6-9195-65363B74027F} = {AA217EB8-E480-456B-BDF3-39419EF2AD85}
{742599E6-2FC2-4B39-85B8-976C98013030} = {7A3C7B26-26A0-49B9-8505-5F8267C10F10}
{7CA2E5FE-E507-4DC6-930C-E18711A9F856} = {92B26F48-235F-4500-BD55-800F06A0BA39}
{83FA86DB-34E4-4C2C-832C-90F491CA10C7} = {92B26F48-235F-4500-BD55-800F06A0BA39}
{3F4F9059-F869-42D3-B92C-90D27ADFC42D} = {92B26F48-235F-4500-BD55-800F06A0BA39}
{A9870842-FE7A-4760-95DC-9D485DDDA31F} = {92B26F48-235F-4500-BD55-800F06A0BA39}
EndGlobalSection
EndGlobal

View File

@ -2,7 +2,7 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
namespace Phantom.Controller.Database.Postgres;
namespace Phantom.Server.Database.Postgres;
public sealed class ApplicationDbContextDesignFactory : IDesignTimeDbContextFactory<ApplicationDbContext> {
public ApplicationDbContext CreateDbContext(string[] args) {

View File

@ -5,11 +5,11 @@ using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Phantom.Controller.Database;
using Phantom.Server.Database;
#nullable disable
namespace Phantom.Controller.Database.Postgres.Migrations
namespace Phantom.Server.Database.Postgres.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20221007033307_Agents")]
@ -25,7 +25,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Phantom.Controller.Database.Entities.AgentEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.AgentEntity", b =>
{
b.Property<Guid>("AgentGuid")
.ValueGeneratedOnAdd()

View File

@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Phantom.Controller.Database.Postgres.Migrations
namespace Phantom.Server.Database.Postgres.Migrations
{
/// <inheritdoc />
public partial class Agents : Migration

View File

@ -5,11 +5,11 @@ using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Phantom.Controller.Database;
using Phantom.Server.Database;
#nullable disable
namespace Phantom.Controller.Database.Postgres.Migrations
namespace Phantom.Server.Database.Postgres.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20221007095438_Instances")]
@ -25,7 +25,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Phantom.Controller.Database.Entities.AgentEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.AgentEntity", b =>
{
b.Property<Guid>("AgentGuid")
.ValueGeneratedOnAdd()
@ -49,7 +49,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
b.ToTable("Agents", "agents");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.InstanceEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.InstanceEntity", b =>
{
b.Property<Guid>("InstanceGuid")
.ValueGeneratedOnAdd()

View File

@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Phantom.Controller.Database.Postgres.Migrations
namespace Phantom.Server.Database.Postgres.Migrations
{
/// <inheritdoc />
public partial class Instances : Migration

View File

@ -5,11 +5,11 @@ using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Phantom.Controller.Database;
using Phantom.Server.Database;
#nullable disable
namespace Phantom.Controller.Database.Postgres.Migrations
namespace Phantom.Server.Database.Postgres.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20221008163849_Identity")]
@ -221,7 +221,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
b.ToTable("UserTokens", "identity");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.AgentEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.AgentEntity", b =>
{
b.Property<Guid>("AgentGuid")
.ValueGeneratedOnAdd()
@ -245,7 +245,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
b.ToTable("Agents", "agents");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.InstanceEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.InstanceEntity", b =>
{
b.Property<Guid>("InstanceGuid")
.ValueGeneratedOnAdd()

View File

@ -4,7 +4,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace Phantom.Controller.Database.Postgres.Migrations
namespace Phantom.Server.Database.Postgres.Migrations
{
/// <inheritdoc />
public partial class Identity : Migration

View File

@ -5,11 +5,11 @@ using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Phantom.Controller.Database;
using Phantom.Server.Database;
#nullable disable
namespace Phantom.Controller.Database.Postgres.Migrations
namespace Phantom.Server.Database.Postgres.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20221010193220_InstanceJvmArguments")]
@ -221,7 +221,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
b.ToTable("UserTokens", "identity");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.AgentEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.AgentEntity", b =>
{
b.Property<Guid>("AgentGuid")
.ValueGeneratedOnAdd()
@ -245,7 +245,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
b.ToTable("Agents", "agents");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.InstanceEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.InstanceEntity", b =>
{
b.Property<Guid>("InstanceGuid")
.ValueGeneratedOnAdd()

View File

@ -2,7 +2,7 @@
#nullable disable
namespace Phantom.Controller.Database.Postgres.Migrations
namespace Phantom.Server.Database.Postgres.Migrations
{
/// <inheritdoc />
public partial class InstanceJvmArguments : Migration

View File

@ -6,11 +6,11 @@ using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Phantom.Controller.Database;
using Phantom.Server.Database;
#nullable disable
namespace Phantom.Controller.Database.Postgres.Migrations
namespace Phantom.Server.Database.Postgres.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20221016035515_AuditLog")]
@ -222,7 +222,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
b.ToTable("UserTokens", "identity");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.AgentEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.AgentEntity", b =>
{
b.Property<Guid>("AgentGuid")
.ValueGeneratedOnAdd()
@ -246,7 +246,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
b.ToTable("Agents", "agents");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.AuditEventEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.AuditEventEntity", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
@ -282,7 +282,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
b.ToTable("AuditEvents", "system");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.InstanceEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.InstanceEntity", b =>
{
b.Property<Guid>("InstanceGuid")
.ValueGeneratedOnAdd()
@ -378,7 +378,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
.IsRequired();
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.AuditEventEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.AuditEventEntity", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User")
.WithMany()

View File

@ -5,7 +5,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace Phantom.Controller.Database.Postgres.Migrations
namespace Phantom.Server.Database.Postgres.Migrations
{
/// <inheritdoc />
public partial class AuditLog : Migration

View File

@ -6,11 +6,11 @@ using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Phantom.Controller.Database;
using Phantom.Server.Database;
#nullable disable
namespace Phantom.Controller.Database.Postgres.Migrations
namespace Phantom.Server.Database.Postgres.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20221018231328_AgentVersion")]
@ -222,7 +222,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
b.ToTable("UserTokens", "identity");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.AgentEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.AgentEntity", b =>
{
b.Property<Guid>("AgentGuid")
.ValueGeneratedOnAdd()
@ -250,7 +250,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
b.ToTable("Agents", "agents");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.AuditEventEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.AuditEventEntity", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
@ -286,7 +286,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
b.ToTable("AuditEvents", "system");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.InstanceEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.InstanceEntity", b =>
{
b.Property<Guid>("InstanceGuid")
.ValueGeneratedOnAdd()
@ -382,7 +382,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
.IsRequired();
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.AuditEventEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.AuditEventEntity", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User")
.WithMany()

View File

@ -2,7 +2,7 @@
#nullable disable
namespace Phantom.Controller.Database.Postgres.Migrations
namespace Phantom.Server.Database.Postgres.Migrations
{
/// <inheritdoc />
public partial class AgentVersion : Migration

View File

@ -6,11 +6,11 @@ using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Phantom.Controller.Database;
using Phantom.Server.Database;
#nullable disable
namespace Phantom.Controller.Database.Postgres.Migrations
namespace Phantom.Server.Database.Postgres.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20221021125232_Permissions")]
@ -222,7 +222,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
b.ToTable("UserTokens", "identity");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.AgentEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.AgentEntity", b =>
{
b.Property<Guid>("AgentGuid")
.ValueGeneratedOnAdd()
@ -250,7 +250,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
b.ToTable("Agents", "agents");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.AuditEventEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.AuditEventEntity", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
@ -286,7 +286,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
b.ToTable("AuditEvents", "system");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.InstanceEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.InstanceEntity", b =>
{
b.Property<Guid>("InstanceGuid")
.ValueGeneratedOnAdd()
@ -331,7 +331,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
b.ToTable("Instances", "agents");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.PermissionEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.PermissionEntity", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
@ -341,7 +341,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
b.ToTable("Permissions", "identity");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.RolePermissionEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.RolePermissionEntity", b =>
{
b.Property<string>("RoleId")
.HasColumnType("text");
@ -356,7 +356,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
b.ToTable("RolePermissions", "identity");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.UserPermissionEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.UserPermissionEntity", b =>
{
b.Property<string>("UserId")
.HasColumnType("text");
@ -422,7 +422,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
.IsRequired();
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.AuditEventEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.AuditEventEntity", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User")
.WithMany()
@ -431,9 +431,9 @@ namespace Phantom.Controller.Database.Postgres.Migrations
b.Navigation("User");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.RolePermissionEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.RolePermissionEntity", b =>
{
b.HasOne("Phantom.Controller.Database.Entities.PermissionEntity", null)
b.HasOne("Phantom.Server.Database.Entities.PermissionEntity", null)
.WithMany()
.HasForeignKey("PermissionId")
.OnDelete(DeleteBehavior.Cascade)
@ -446,9 +446,9 @@ namespace Phantom.Controller.Database.Postgres.Migrations
.IsRequired();
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.UserPermissionEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.UserPermissionEntity", b =>
{
b.HasOne("Phantom.Controller.Database.Entities.PermissionEntity", null)
b.HasOne("Phantom.Server.Database.Entities.PermissionEntity", null)
.WithMany()
.HasForeignKey("PermissionId")
.OnDelete(DeleteBehavior.Cascade)

View File

@ -2,7 +2,7 @@
#nullable disable
namespace Phantom.Controller.Database.Postgres.Migrations
namespace Phantom.Server.Database.Postgres.Migrations
{
/// <inheritdoc />
public partial class Permissions : Migration

View File

@ -6,11 +6,11 @@ using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Phantom.Controller.Database;
using Phantom.Server.Database;
#nullable disable
namespace Phantom.Controller.Database.Postgres.Migrations
namespace Phantom.Server.Database.Postgres.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20230213040522_AuditLogRename")]
@ -222,7 +222,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
b.ToTable("UserTokens", "identity");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.AgentEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.AgentEntity", b =>
{
b.Property<Guid>("AgentGuid")
.ValueGeneratedOnAdd()
@ -250,7 +250,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
b.ToTable("Agents", "agents");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.AuditEventEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.AuditEventEntity", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
@ -286,7 +286,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
b.ToTable("AuditLog", "system");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.InstanceEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.InstanceEntity", b =>
{
b.Property<Guid>("InstanceGuid")
.ValueGeneratedOnAdd()
@ -331,7 +331,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
b.ToTable("Instances", "agents");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.PermissionEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.PermissionEntity", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
@ -341,7 +341,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
b.ToTable("Permissions", "identity");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.RolePermissionEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.RolePermissionEntity", b =>
{
b.Property<string>("RoleId")
.HasColumnType("text");
@ -356,7 +356,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
b.ToTable("RolePermissions", "identity");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.UserPermissionEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.UserPermissionEntity", b =>
{
b.Property<string>("UserId")
.HasColumnType("text");
@ -422,7 +422,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
.IsRequired();
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.AuditEventEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.AuditEventEntity", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User")
.WithMany()
@ -431,9 +431,9 @@ namespace Phantom.Controller.Database.Postgres.Migrations
b.Navigation("User");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.RolePermissionEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.RolePermissionEntity", b =>
{
b.HasOne("Phantom.Controller.Database.Entities.PermissionEntity", null)
b.HasOne("Phantom.Server.Database.Entities.PermissionEntity", null)
.WithMany()
.HasForeignKey("PermissionId")
.OnDelete(DeleteBehavior.Cascade)
@ -446,9 +446,9 @@ namespace Phantom.Controller.Database.Postgres.Migrations
.IsRequired();
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.UserPermissionEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.UserPermissionEntity", b =>
{
b.HasOne("Phantom.Controller.Database.Entities.PermissionEntity", null)
b.HasOne("Phantom.Server.Database.Entities.PermissionEntity", null)
.WithMany()
.HasForeignKey("PermissionId")
.OnDelete(DeleteBehavior.Cascade)

View File

@ -2,7 +2,7 @@
#nullable disable
namespace Phantom.Controller.Database.Postgres.Migrations
namespace Phantom.Server.Database.Postgres.Migrations
{
/// <inheritdoc />
public partial class AuditLogRename : Migration

View File

@ -6,11 +6,11 @@ using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Phantom.Controller.Database;
using Phantom.Server.Database;
#nullable disable
namespace Phantom.Controller.Database.Postgres.Migrations
namespace Phantom.Server.Database.Postgres.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20230215101444_EventLog")]
@ -222,7 +222,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
b.ToTable("UserTokens", "identity");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.AgentEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.AgentEntity", b =>
{
b.Property<Guid>("AgentGuid")
.ValueGeneratedOnAdd()
@ -250,7 +250,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
b.ToTable("Agents", "agents");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.AuditLogEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.AuditLogEntity", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
@ -286,7 +286,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
b.ToTable("AuditLog", "system");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.EventLogEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.EventLogEntity", b =>
{
b.Property<Guid>("EventGuid")
.ValueGeneratedOnAdd()
@ -318,7 +318,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
b.ToTable("EventLog", "system");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.InstanceEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.InstanceEntity", b =>
{
b.Property<Guid>("InstanceGuid")
.ValueGeneratedOnAdd()
@ -363,7 +363,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
b.ToTable("Instances", "agents");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.PermissionEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.PermissionEntity", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
@ -373,7 +373,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
b.ToTable("Permissions", "identity");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.RolePermissionEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.RolePermissionEntity", b =>
{
b.Property<string>("RoleId")
.HasColumnType("text");
@ -388,7 +388,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
b.ToTable("RolePermissions", "identity");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.UserPermissionEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.UserPermissionEntity", b =>
{
b.Property<string>("UserId")
.HasColumnType("text");
@ -454,7 +454,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
.IsRequired();
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.AuditLogEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.AuditLogEntity", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User")
.WithMany()
@ -463,9 +463,9 @@ namespace Phantom.Controller.Database.Postgres.Migrations
b.Navigation("User");
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.RolePermissionEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.RolePermissionEntity", b =>
{
b.HasOne("Phantom.Controller.Database.Entities.PermissionEntity", null)
b.HasOne("Phantom.Server.Database.Entities.PermissionEntity", null)
.WithMany()
.HasForeignKey("PermissionId")
.OnDelete(DeleteBehavior.Cascade)
@ -478,9 +478,9 @@ namespace Phantom.Controller.Database.Postgres.Migrations
.IsRequired();
});
modelBuilder.Entity("Phantom.Controller.Database.Entities.UserPermissionEntity", b =>
modelBuilder.Entity("Phantom.Server.Database.Entities.UserPermissionEntity", b =>
{
b.HasOne("Phantom.Controller.Database.Entities.PermissionEntity", null)
b.HasOne("Phantom.Server.Database.Entities.PermissionEntity", null)
.WithMany()
.HasForeignKey("PermissionId")
.OnDelete(DeleteBehavior.Cascade)

View File

@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Phantom.Controller.Database.Postgres.Migrations
namespace Phantom.Server.Database.Postgres.Migrations
{
/// <inheritdoc />
public partial class EventLog : Migration

View File

@ -0,0 +1,495 @@
// <auto-generated />
using System;
using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Phantom.Server.Database;
#nullable disable
namespace Phantom.Server.Database.Postgres.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
partial class ApplicationDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "7.0.1")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("text");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex");
b.ToTable("Roles", "identity");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("text");
b.Property<string>("ClaimValue")
.HasColumnType("text");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("RoleClaims", "identity");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<int>("AccessFailedCount")
.HasColumnType("integer");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("text");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("boolean");
b.Property<bool>("LockoutEnabled")
.HasColumnType("boolean");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("timestamp with time zone");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("PasswordHash")
.HasColumnType("text");
b.Property<string>("PhoneNumber")
.HasColumnType("text");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("boolean");
b.Property<string>("SecurityStamp")
.HasColumnType("text");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("boolean");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex");
b.ToTable("Users", "identity");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("text");
b.Property<string>("ClaimValue")
.HasColumnType("text");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("UserClaims", "identity");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("text");
b.Property<string>("ProviderKey")
.HasColumnType("text");
b.Property<string>("ProviderDisplayName")
.HasColumnType("text");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("text");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("UserLogins", "identity");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("text");
b.Property<string>("RoleId")
.HasColumnType("text");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("UserRoles", "identity");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("text");
b.Property<string>("LoginProvider")
.HasColumnType("text");
b.Property<string>("Name")
.HasColumnType("text");
b.Property<string>("Value")
.HasColumnType("text");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("UserTokens", "identity");
});
modelBuilder.Entity("Phantom.Server.Database.Entities.AgentEntity", b =>
{
b.Property<Guid>("AgentGuid")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("BuildVersion")
.IsRequired()
.HasColumnType("text");
b.Property<int>("MaxInstances")
.HasColumnType("integer");
b.Property<ushort>("MaxMemory")
.HasColumnType("integer");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<int>("ProtocolVersion")
.HasColumnType("integer");
b.HasKey("AgentGuid");
b.ToTable("Agents", "agents");
});
modelBuilder.Entity("Phantom.Server.Database.Entities.AuditLogEntity", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<JsonDocument>("Data")
.HasColumnType("jsonb");
b.Property<string>("EventType")
.IsRequired()
.HasColumnType("text");
b.Property<string>("SubjectId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("SubjectType")
.IsRequired()
.HasColumnType("text");
b.Property<string>("UserId")
.HasColumnType("text");
b.Property<DateTime>("UtcTime")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AuditLog", "system");
});
modelBuilder.Entity("Phantom.Server.Database.Entities.EventLogEntity", b =>
{
b.Property<Guid>("EventGuid")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid?>("AgentGuid")
.HasColumnType("uuid");
b.Property<JsonDocument>("Data")
.HasColumnType("jsonb");
b.Property<string>("EventType")
.IsRequired()
.HasColumnType("text");
b.Property<string>("SubjectId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("SubjectType")
.IsRequired()
.HasColumnType("text");
b.Property<DateTime>("UtcTime")
.HasColumnType("timestamp with time zone");
b.HasKey("EventGuid");
b.ToTable("EventLog", "system");
});
modelBuilder.Entity("Phantom.Server.Database.Entities.InstanceEntity", b =>
{
b.Property<Guid>("InstanceGuid")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid>("AgentGuid")
.HasColumnType("uuid");
b.Property<string>("InstanceName")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("JavaRuntimeGuid")
.HasColumnType("uuid");
b.Property<string>("JvmArguments")
.IsRequired()
.HasColumnType("text");
b.Property<bool>("LaunchAutomatically")
.HasColumnType("boolean");
b.Property<ushort>("MemoryAllocation")
.HasColumnType("integer");
b.Property<string>("MinecraftServerKind")
.IsRequired()
.HasColumnType("text");
b.Property<string>("MinecraftVersion")
.IsRequired()
.HasColumnType("text");
b.Property<int>("RconPort")
.HasColumnType("integer");
b.Property<int>("ServerPort")
.HasColumnType("integer");
b.HasKey("InstanceGuid");
b.ToTable("Instances", "agents");
});
modelBuilder.Entity("Phantom.Server.Database.Entities.PermissionEntity", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Permissions", "identity");
});
modelBuilder.Entity("Phantom.Server.Database.Entities.RolePermissionEntity", b =>
{
b.Property<string>("RoleId")
.HasColumnType("text");
b.Property<string>("PermissionId")
.HasColumnType("text");
b.HasKey("RoleId", "PermissionId");
b.HasIndex("PermissionId");
b.ToTable("RolePermissions", "identity");
});
modelBuilder.Entity("Phantom.Server.Database.Entities.UserPermissionEntity", b =>
{
b.Property<string>("UserId")
.HasColumnType("text");
b.Property<string>("PermissionId")
.HasColumnType("text");
b.HasKey("UserId", "PermissionId");
b.HasIndex("PermissionId");
b.ToTable("UserPermissions", "identity");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Phantom.Server.Database.Entities.AuditLogEntity", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User")
.WithMany()
.HasForeignKey("UserId");
b.Navigation("User");
});
modelBuilder.Entity("Phantom.Server.Database.Entities.RolePermissionEntity", b =>
{
b.HasOne("Phantom.Server.Database.Entities.PermissionEntity", null)
.WithMany()
.HasForeignKey("PermissionId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Phantom.Server.Database.Entities.UserPermissionEntity", b =>
{
b.HasOne("Phantom.Server.Database.Entities.PermissionEntity", null)
.WithMany()
.HasForeignKey("PermissionId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
#pragma warning restore 612, 618
}
}
}

View File

@ -14,7 +14,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Phantom.Controller.Database\Phantom.Controller.Database.csproj" />
<ProjectReference Include="..\Phantom.Server.Database\Phantom.Server.Database.csproj" />
</ItemGroup>
</Project>

View File

@ -1,22 +1,20 @@
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Phantom.Common.Data;
using Phantom.Common.Data.Minecraft;
using Phantom.Controller.Database.Converters;
using Phantom.Controller.Database.Entities;
using Phantom.Controller.Database.Enums;
using Phantom.Controller.Database.Factories;
using Phantom.Server.Database.Converters;
using Phantom.Server.Database.Entities;
using Phantom.Server.Database.Enums;
using Phantom.Server.Database.Factories;
namespace Phantom.Controller.Database;
namespace Phantom.Server.Database;
[SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Global")]
public class ApplicationDbContext : DbContext {
public DbSet<UserEntity> Users { get; set; } = null!;
public DbSet<RoleEntity> Roles { get; set; } = null!;
public class ApplicationDbContext : IdentityDbContext {
public DbSet<PermissionEntity> Permissions { get; set; } = null!;
public DbSet<UserRoleEntity> UserRoles { get; set; } = null!;
public DbSet<UserPermissionEntity> UserPermissions { get; set; } = null!;
public DbSet<RolePermissionEntity> RolePermissions { get; set; } = null!;
@ -36,29 +34,26 @@ public class ApplicationDbContext : DbContext {
protected override void OnModelCreating(ModelBuilder builder) {
base.OnModelCreating(builder);
builder.Entity<AuditLogEntity>(static b => {
b.HasOne(static e => e.User).WithMany().HasForeignKey(static e => e.UserGuid).IsRequired(false).OnDelete(DeleteBehavior.SetNull);
});
const string IdentitySchema = "identity";
builder.Entity<UserEntity>(static b => {
b.HasIndex(static e => e.Name).IsUnique();
});
builder.Entity<IdentityRole>().ToTable("Roles", schema: IdentitySchema);
builder.Entity<IdentityRoleClaim<string>>().ToTable("RoleClaims", schema: IdentitySchema);
builder.Entity<UserRoleEntity>(static b => {
b.HasKey(static e => new { UserId = e.UserGuid, RoleId = e.RoleGuid });
b.HasOne(static e => e.User).WithMany().HasForeignKey(static e => e.UserGuid).IsRequired().OnDelete(DeleteBehavior.Cascade);
b.HasOne(static e => e.Role).WithMany().HasForeignKey(static e => e.RoleGuid).IsRequired().OnDelete(DeleteBehavior.Cascade);
});
builder.Entity<IdentityUser>().ToTable("Users", schema: IdentitySchema);
builder.Entity<IdentityUserRole<string>>().ToTable("UserRoles", schema: IdentitySchema);
builder.Entity<IdentityUserLogin<string>>().ToTable("UserLogins", schema: IdentitySchema);
builder.Entity<IdentityUserToken<string>>().ToTable("UserTokens", schema: IdentitySchema);
builder.Entity<IdentityUserClaim<string>>().ToTable("UserClaims", schema: IdentitySchema);
builder.Entity<UserPermissionEntity>(static b => {
b.HasKey(static e => new { UserId = e.UserGuid, e.PermissionId });
b.HasOne<UserEntity>().WithMany().HasForeignKey(static e => e.UserGuid).IsRequired().OnDelete(DeleteBehavior.Cascade);
b.HasKey(static e => new { e.UserId, e.PermissionId });
b.HasOne<IdentityUser>().WithMany().HasForeignKey(static e => e.UserId).IsRequired().OnDelete(DeleteBehavior.Cascade);
b.HasOne<PermissionEntity>().WithMany().HasForeignKey(static e => e.PermissionId).IsRequired().OnDelete(DeleteBehavior.Cascade);
});
builder.Entity<RolePermissionEntity>(static b => {
b.HasKey(static e => new { RoleId = e.RoleGuid, e.PermissionId });
b.HasOne<RoleEntity>().WithMany().HasForeignKey(static e => e.RoleGuid).IsRequired().OnDelete(DeleteBehavior.Cascade);
b.HasKey(static e => new { e.RoleId, e.PermissionId });
b.HasOne<IdentityRole>().WithMany().HasForeignKey(static e => e.RoleId).IsRequired().OnDelete(DeleteBehavior.Cascade);
b.HasOne<PermissionEntity>().WithMany().HasForeignKey(static e => e.PermissionId).IsRequired().OnDelete(DeleteBehavior.Cascade);
});
}

View File

@ -2,7 +2,7 @@
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Phantom.Common.Data;
namespace Phantom.Controller.Database.Converters;
namespace Phantom.Server.Database.Converters;
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
sealed class RamAllocationUnitsConverter : ValueConverter<RamAllocationUnits, ushort> {

View File

@ -1,6 +1,6 @@
using Microsoft.Extensions.DependencyInjection;
namespace Phantom.Controller.Database;
namespace Phantom.Server.Database;
public sealed class DatabaseProvider {
private readonly IServiceScopeFactory serviceScopeFactory;

View File

@ -3,7 +3,7 @@ using System.ComponentModel.DataAnnotations.Schema;
using System.Diagnostics.CodeAnalysis;
using Phantom.Common.Data;
namespace Phantom.Controller.Database.Entities;
namespace Phantom.Server.Database.Entities;
[Table("Agents", Schema = "agents")]
[SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Global")]

View File

@ -2,9 +2,10 @@
using System.ComponentModel.DataAnnotations.Schema;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
using Phantom.Controller.Database.Enums;
using Microsoft.AspNetCore.Identity;
using Phantom.Server.Database.Enums;
namespace Phantom.Controller.Database.Entities;
namespace Phantom.Server.Database.Entities;
[Table("AuditLog", Schema = "system")]
[SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Global")]
@ -15,22 +16,22 @@ public class AuditLogEntity : IDisposable {
[SuppressMessage("ReSharper", "UnusedMember.Global")]
public long Id { get; set; }
public Guid? UserGuid { get; set; }
public string? UserId { get; set; }
public DateTime UtcTime { get; set; } // Note: Converting to UTC is not best practice, but for historical records it's good enough.
public AuditLogEventType EventType { get; set; }
public AuditLogSubjectType SubjectType { get; set; }
public string SubjectId { get; set; }
public JsonDocument? Data { get; set; }
public virtual UserEntity? User { get; set; }
public virtual IdentityUser? User { get; set; }
[SuppressMessage("ReSharper", "UnusedMember.Global")]
internal AuditLogEntity() {
SubjectId = string.Empty;
}
public AuditLogEntity(Guid? userGuid, AuditLogEventType eventType, string subjectId, Dictionary<string, object?>? data) {
UserGuid = userGuid;
public AuditLogEntity(string? userId, AuditLogEventType eventType, string subjectId, Dictionary<string, object?>? data) {
UserId = userId;
UtcTime = DateTime.UtcNow;
EventType = eventType;
SubjectType = eventType.GetSubjectType();

View File

@ -2,9 +2,9 @@
using System.ComponentModel.DataAnnotations.Schema;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
using Phantom.Controller.Database.Enums;
using Phantom.Server.Database.Enums;
namespace Phantom.Controller.Database.Entities;
namespace Phantom.Server.Database.Entities;
[Table("EventLog", Schema = "system")]
[SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Global")]

View File

@ -4,7 +4,7 @@ using System.Diagnostics.CodeAnalysis;
using Phantom.Common.Data;
using Phantom.Common.Data.Minecraft;
namespace Phantom.Controller.Database.Entities;
namespace Phantom.Server.Database.Entities;
[Table("Instances", Schema = "agents")]
[SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Global")]

View File

@ -1,7 +1,7 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Phantom.Controller.Database.Entities;
namespace Phantom.Server.Database.Entities;
[Table("Permissions", Schema = "identity")]
public sealed class PermissionEntity {

View File

@ -1,14 +1,14 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace Phantom.Controller.Database.Entities;
namespace Phantom.Server.Database.Entities;
[Table("RolePermissions", Schema = "identity")]
public sealed class RolePermissionEntity {
public Guid RoleGuid { get; set; }
public string RoleId { get; set; }
public string PermissionId { get; set; }
public RolePermissionEntity(Guid roleGuid, string permissionId) {
RoleGuid = roleGuid;
public RolePermissionEntity(string roleId, string permissionId) {
RoleId = roleId;
PermissionId = permissionId;
}
}

View File

@ -1,14 +1,14 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace Phantom.Controller.Database.Entities;
namespace Phantom.Server.Database.Entities;
[Table("UserPermissions", Schema = "identity")]
public sealed class UserPermissionEntity {
public Guid UserGuid { get; set; }
public string UserId { get; set; }
public string PermissionId { get; set; }
public UserPermissionEntity(Guid userGuid, string permissionId) {
UserGuid = userGuid;
public UserPermissionEntity(string userId, string permissionId) {
UserId = userId;
PermissionId = permissionId;
}
}

View File

@ -1,4 +1,4 @@
namespace Phantom.Controller.Database.Enums;
namespace Phantom.Server.Database.Enums;
public enum AuditLogEventType {
AdministratorUserCreated,

View File

@ -1,4 +1,4 @@
namespace Phantom.Controller.Database.Enums;
namespace Phantom.Server.Database.Enums;
public enum AuditLogSubjectType {
User,

View File

@ -1,4 +1,4 @@
namespace Phantom.Controller.Database.Enums;
namespace Phantom.Server.Database.Enums;
public enum EventLogEventType {
InstanceLaunchSucceded,

View File

@ -0,0 +1,5 @@
namespace Phantom.Server.Database.Enums;
public enum EventLogSubjectType {
Instance
}

View File

@ -1,6 +1,6 @@
using Microsoft.EntityFrameworkCore;
namespace Phantom.Controller.Database.Factories;
namespace Phantom.Server.Database.Factories;
public abstract class AbstractUpsertHelper<T> where T : class {
private protected readonly ApplicationDbContext Ctx;

View File

@ -1,7 +1,7 @@
using Microsoft.EntityFrameworkCore;
using Phantom.Controller.Database.Entities;
using Phantom.Server.Database.Entities;
namespace Phantom.Controller.Database.Factories;
namespace Phantom.Server.Database.Factories;
public sealed class AgentEntityUpsert : AbstractUpsertHelper<AgentEntity> {
internal AgentEntityUpsert(ApplicationDbContext ctx) : base(ctx) {}

View File

@ -1,7 +1,7 @@
using Microsoft.EntityFrameworkCore;
using Phantom.Controller.Database.Entities;
using Phantom.Server.Database.Entities;
namespace Phantom.Controller.Database.Factories;
namespace Phantom.Server.Database.Factories;
public sealed class InstanceEntityUpsert : AbstractUpsertHelper<InstanceEntity> {
internal InstanceEntityUpsert(ApplicationDbContext ctx) : base(ctx) {}

View File

@ -6,7 +6,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View File

@ -1,6 +1,6 @@
using System.Collections.Immutable;
namespace Phantom.Controller.Minecraft;
namespace Phantom.Server.Minecraft;
public static class JvmArgumentsHelper {
public static ImmutableArray<string> Split(string arguments) {

View File

@ -8,7 +8,7 @@ using Phantom.Utils.IO;
using Phantom.Utils.Runtime;
using Serilog;
namespace Phantom.Controller.Minecraft;
namespace Phantom.Server.Minecraft;
sealed class MinecraftVersionApi : IDisposable {
private static readonly ILogger Logger = PhantomLogger.Create<MinecraftVersionApi>();

View File

@ -4,7 +4,7 @@ using Phantom.Common.Data.Minecraft;
using Phantom.Common.Logging;
using Serilog;
namespace Phantom.Controller.Minecraft;
namespace Phantom.Server.Minecraft;
public sealed class MinecraftVersions : IDisposable {
private static readonly ILogger Logger = PhantomLogger.Create<MinecraftVersions>();

View File

@ -4,7 +4,7 @@ using Phantom.Common.Messages;
using Phantom.Common.Messages.BiDirectional;
using Phantom.Utils.Rpc.Message;
namespace Phantom.Controller.Rpc;
namespace Phantom.Server.Rpc;
public sealed class RpcClientConnection {
private readonly ServerSocket socket;

View File

@ -1,4 +1,4 @@
namespace Phantom.Controller.Rpc;
namespace Phantom.Server.Rpc;
sealed class RpcClientConnectionClosedEventArgs : EventArgs {
public uint RoutingId { get; }

View File

@ -8,7 +8,7 @@ using Phantom.Utils.Tasks;
using Serilog;
using Serilog.Events;
namespace Phantom.Controller.Rpc;
namespace Phantom.Server.Rpc;
public sealed class RpcLauncher : RpcRuntime<ServerSocket> {
public static Task Launch(RpcConfiguration config, Func<RpcClientConnection, IMessageToServerListener> listenerFactory, CancellationToken cancellationToken) {

View File

@ -1,7 +1,7 @@
using Phantom.Common.Data;
using Phantom.Common.Data.Agent;
namespace Phantom.Controller.Services.Agents;
namespace Phantom.Server.Services.Agents;
public sealed record Agent(
Guid Guid,

View File

@ -1,7 +1,7 @@
using Phantom.Common.Messages;
using Phantom.Controller.Rpc;
using Phantom.Server.Rpc;
namespace Phantom.Controller.Services.Agents;
namespace Phantom.Server.Services.Agents;
sealed class AgentConnection {
private readonly RpcClientConnection connection;

View File

@ -2,7 +2,7 @@
using Phantom.Common.Data.Java;
using Phantom.Utils.Collections;
namespace Phantom.Controller.Services.Agents;
namespace Phantom.Server.Services.Agents;
public sealed class AgentJavaRuntimesManager {
private readonly RwLockedDictionary<Guid, ImmutableArray<TaggedJavaRuntime>> runtimes = new (LockRecursionPolicy.NoRecursion);

View File

@ -5,15 +5,15 @@ using Phantom.Common.Data.Replies;
using Phantom.Common.Logging;
using Phantom.Common.Messages;
using Phantom.Common.Messages.ToAgent;
using Phantom.Controller.Database;
using Phantom.Controller.Rpc;
using Phantom.Controller.Services.Instances;
using Phantom.Server.Database;
using Phantom.Server.Rpc;
using Phantom.Server.Services.Instances;
using Phantom.Utils.Collections;
using Phantom.Utils.Events;
using Phantom.Utils.Tasks;
using ILogger = Serilog.ILogger;
namespace Phantom.Controller.Services.Agents;
namespace Phantom.Server.Services.Agents;
public sealed class AgentManager {
private static readonly ILogger Logger = PhantomLogger.Create<AgentManager>();

View File

@ -1,6 +1,6 @@
using Phantom.Common.Data;
namespace Phantom.Controller.Services.Agents;
namespace Phantom.Server.Services.Agents;
public sealed record AgentStats(
int RunningInstanceCount,

View File

@ -1,30 +1,30 @@
using Phantom.Controller.Database.Entities;
using Phantom.Controller.Database.Enums;
using Microsoft.AspNetCore.Identity;
using Phantom.Server.Database.Enums;
namespace Phantom.Controller.Services.Audit;
namespace Phantom.Server.Services.Audit;
public sealed partial class AuditLog {
public Task AddAdministratorUserCreatedEvent(UserEntity administratorUser) {
return AddItem(AuditLogEventType.AdministratorUserCreated, administratorUser.UserGuid.ToString());
public Task AddAdministratorUserCreatedEvent(IdentityUser administratorUser) {
return AddItem(AuditLogEventType.AdministratorUserCreated, administratorUser.Id);
}
public Task AddAdministratorUserModifiedEvent(UserEntity administratorUser) {
return AddItem(AuditLogEventType.AdministratorUserModified, administratorUser.UserGuid.ToString());
public Task AddAdministratorUserModifiedEvent(IdentityUser administratorUser) {
return AddItem(AuditLogEventType.AdministratorUserModified, administratorUser.Id);
}
public void AddUserLoggedInEvent(UserEntity user) {
AddItem(user.UserGuid, AuditLogEventType.UserLoggedIn, user.UserGuid.ToString());
public void AddUserLoggedInEvent(string userId) {
AddItem(userId, AuditLogEventType.UserLoggedIn, userId);
}
public void AddUserLoggedOutEvent(Guid userGuid) {
AddItem(userGuid, AuditLogEventType.UserLoggedOut, userGuid.ToString());
public void AddUserLoggedOutEvent(string userId) {
AddItem(userId, AuditLogEventType.UserLoggedOut, userId);
}
public Task AddUserCreatedEvent(UserEntity user) {
return AddItem(AuditLogEventType.UserCreated, user.UserGuid.ToString());
public Task AddUserCreatedEvent(IdentityUser user) {
return AddItem(AuditLogEventType.UserCreated, user.Id);
}
public Task AddUserRolesChangedEvent(UserEntity user, List<string> addedToRoles, List<string> removedFromRoles) {
public Task AddUserRolesChangedEvent(IdentityUser user, List<string> addedToRoles, List<string> removedFromRoles) {
var extra = new Dictionary<string, object?>();
if (addedToRoles.Count > 0) {
@ -35,12 +35,12 @@ public sealed partial class AuditLog {
extra["removedFromRoles"] = removedFromRoles;
}
return AddItem(AuditLogEventType.UserRolesChanged, user.UserGuid.ToString(), extra);
return AddItem(AuditLogEventType.UserRolesChanged, user.Id, extra);
}
public Task AddUserDeletedEvent(UserEntity user) {
return AddItem(AuditLogEventType.UserDeleted, user.UserGuid.ToString(), new Dictionary<string, object?> {
{ "username", user.Name }
public Task AddUserDeletedEvent(IdentityUser user) {
return AddItem(AuditLogEventType.UserDeleted, user.Id, new Dictionary<string, object?> {
{ "username", user.UserName }
});
}

View File

@ -1,29 +1,31 @@
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.EntityFrameworkCore;
using Phantom.Controller.Database;
using Phantom.Controller.Database.Entities;
using Phantom.Controller.Database.Enums;
using Phantom.Controller.Services.Users;
using Phantom.Server.Database;
using Phantom.Server.Database.Entities;
using Phantom.Server.Database.Enums;
using Phantom.Server.Services.Users;
using Phantom.Utils.Tasks;
namespace Phantom.Controller.Services.Audit;
namespace Phantom.Server.Services.Audit;
public sealed partial class AuditLog {
private readonly CancellationToken cancellationToken;
private readonly DatabaseProvider databaseProvider;
private readonly IdentityLookup identityLookup;
private readonly AuthenticationStateProvider authenticationStateProvider;
private readonly TaskManager taskManager;
public AuditLog(ServiceConfiguration serviceConfiguration, DatabaseProvider databaseProvider, AuthenticationStateProvider authenticationStateProvider, TaskManager taskManager) {
public AuditLog(ServiceConfiguration serviceConfiguration, DatabaseProvider databaseProvider, IdentityLookup identityLookup, AuthenticationStateProvider authenticationStateProvider, TaskManager taskManager) {
this.cancellationToken = serviceConfiguration.CancellationToken;
this.databaseProvider = databaseProvider;
this.identityLookup = identityLookup;
this.authenticationStateProvider = authenticationStateProvider;
this.taskManager = taskManager;
}
private async Task<Guid?> GetCurrentAuthenticatedUserId() {
private async Task<string?> GetCurrentAuthenticatedUserId() {
var authenticationState = await authenticationStateProvider.GetAuthenticationStateAsync();
return UserManager.GetAuthenticatedUserId(authenticationState.User);
return identityLookup.GetAuthenticatedUserId(authenticationState.User);
}
private async Task AddEntityToDatabase(AuditLogEntity logEntity) {
@ -32,8 +34,8 @@ public sealed partial class AuditLog {
await scope.Ctx.SaveChangesAsync(cancellationToken);
}
private void AddItem(Guid? userGuid, AuditLogEventType eventType, string subjectId, Dictionary<string, object?>? extra = null) {
var logEntity = new AuditLogEntity(userGuid, eventType, subjectId, extra);
private void AddItem(string? userId, AuditLogEventType eventType, string subjectId, Dictionary<string, object?>? extra = null) {
var logEntity = new AuditLogEntity(userId, eventType, subjectId, extra);
taskManager.Run("Store audit log item to database", () => AddEntityToDatabase(logEntity));
}
@ -48,7 +50,7 @@ public sealed partial class AuditLog {
.AsQueryable()
.OrderByDescending(static entity => entity.UtcTime)
.Take(count)
.Select(static entity => new AuditLogItem(entity.UtcTime, entity.UserGuid, entity.User == null ? null : entity.User.Name, entity.EventType, entity.SubjectType, entity.SubjectId, entity.Data))
.Select(static entity => new AuditLogItem(entity.UtcTime, entity.UserId, entity.User == null ? null : entity.User.UserName, entity.EventType, entity.SubjectType, entity.SubjectId, entity.Data))
.ToArrayAsync(cancellationToken);
}
}

View File

@ -0,0 +1,6 @@
using System.Text.Json;
using Phantom.Server.Database.Enums;
namespace Phantom.Server.Services.Audit;
public sealed record AuditLogItem(DateTime UtcTime, string? UserId, string? UserName, AuditLogEventType EventType, AuditLogSubjectType SubjectType, string? SubjectId, JsonDocument? Data);

View File

@ -1,8 +1,8 @@
using Phantom.Common.Data.Backups;
using Phantom.Common.Data.Instance;
using Phantom.Controller.Database.Enums;
using Phantom.Server.Database.Enums;
namespace Phantom.Controller.Services.Events;
namespace Phantom.Server.Services.Events;
public sealed partial class EventLog {
internal IInstanceEventVisitor CreateInstanceEventVisitor(Guid eventGuid, DateTime utcTime, Guid agentGuid, Guid instanceGuid) {

View File

@ -1,10 +1,10 @@
using Microsoft.EntityFrameworkCore;
using Phantom.Controller.Database;
using Phantom.Controller.Database.Entities;
using Phantom.Controller.Database.Enums;
using Phantom.Server.Database;
using Phantom.Server.Database.Entities;
using Phantom.Server.Database.Enums;
using Phantom.Utils.Tasks;
namespace Phantom.Controller.Services.Events;
namespace Phantom.Server.Services.Events;
public sealed partial class EventLog {
private readonly CancellationToken cancellationToken;

View File

@ -1,6 +1,6 @@
using System.Text.Json;
using Phantom.Controller.Database.Enums;
using Phantom.Server.Database.Enums;
namespace Phantom.Controller.Services.Events;
namespace Phantom.Server.Services.Events;
public sealed record EventLogItem(DateTime UtcTime, Guid? AgentGuid, EventLogEventType EventType, EventLogSubjectType SubjectType, string SubjectId, JsonDocument? Data);

View File

@ -1,4 +1,4 @@
namespace Phantom.Controller.Services.Instances;
namespace Phantom.Server.Services.Instances;
public enum AddOrEditInstanceResult : byte {
UnknownError,

View File

@ -1,6 +1,6 @@
using Phantom.Common.Data.Instance;
namespace Phantom.Controller.Services.Instances;
namespace Phantom.Server.Services.Instances;
public sealed record Instance(
InstanceConfiguration Configuration,

View File

@ -1,7 +1,7 @@
using System.Net;
using System.Text.RegularExpressions;
namespace Phantom.Controller.Services.Instances;
namespace Phantom.Server.Services.Instances;
static partial class InstanceLogHtmlFilters {
/// <summary>

View File

@ -5,7 +5,7 @@ using Phantom.Utils.Collections;
using Phantom.Utils.Events;
using ILogger = Serilog.ILogger;
namespace Phantom.Controller.Services.Instances;
namespace Phantom.Server.Services.Instances;
public sealed class InstanceLogManager {
private const int RetainedLines = 1000;

View File

@ -2,20 +2,21 @@
using System.Diagnostics.CodeAnalysis;
using Phantom.Common.Data;
using Phantom.Common.Data.Instance;
using Phantom.Common.Data.Java;
using Phantom.Common.Data.Minecraft;
using Phantom.Common.Data.Replies;
using Phantom.Common.Logging;
using Phantom.Common.Messages;
using Phantom.Common.Messages.ToAgent;
using Phantom.Controller.Database;
using Phantom.Controller.Database.Entities;
using Phantom.Controller.Minecraft;
using Phantom.Controller.Services.Agents;
using Phantom.Server.Database;
using Phantom.Server.Database.Entities;
using Phantom.Server.Minecraft;
using Phantom.Server.Services.Agents;
using Phantom.Utils.Collections;
using Phantom.Utils.Events;
using ILogger = Serilog.ILogger;
namespace Phantom.Controller.Services.Instances;
namespace Phantom.Server.Services.Instances;
public sealed class InstanceManager {
private static readonly ILogger Logger = PhantomLogger.Create<InstanceManager>();

View File

@ -12,9 +12,9 @@
<ItemGroup>
<ProjectReference Include="..\..\Common\Phantom.Common.Data\Phantom.Common.Data.csproj" />
<ProjectReference Include="..\..\Utils\Phantom.Utils.Events\Phantom.Utils.Events.csproj" />
<ProjectReference Include="..\Phantom.Controller.Database\Phantom.Controller.Database.csproj" />
<ProjectReference Include="..\Phantom.Controller.Minecraft\Phantom.Controller.Minecraft.csproj" />
<ProjectReference Include="..\Phantom.Controller.Rpc\Phantom.Controller.Rpc.csproj" />
<ProjectReference Include="..\Phantom.Server.Database\Phantom.Server.Database.csproj" />
<ProjectReference Include="..\Phantom.Server.Minecraft\Phantom.Server.Minecraft.csproj" />
<ProjectReference Include="..\Phantom.Server.Rpc\Phantom.Server.Rpc.csproj" />
</ItemGroup>
</Project>

View File

@ -4,14 +4,14 @@ using Phantom.Common.Messages;
using Phantom.Common.Messages.BiDirectional;
using Phantom.Common.Messages.ToAgent;
using Phantom.Common.Messages.ToServer;
using Phantom.Controller.Rpc;
using Phantom.Controller.Services.Agents;
using Phantom.Controller.Services.Events;
using Phantom.Controller.Services.Instances;
using Phantom.Server.Rpc;
using Phantom.Server.Services.Agents;
using Phantom.Server.Services.Events;
using Phantom.Server.Services.Instances;
using Phantom.Utils.Rpc.Message;
using Phantom.Utils.Tasks;
namespace Phantom.Controller.Services.Rpc;
namespace Phantom.Server.Services.Rpc;
public sealed class MessageToServerListener : IMessageToServerListener {
private readonly RpcClientConnection connection;

View File

@ -1,9 +1,9 @@
using Phantom.Controller.Rpc;
using Phantom.Controller.Services.Agents;
using Phantom.Controller.Services.Events;
using Phantom.Controller.Services.Instances;
using Phantom.Server.Rpc;
using Phantom.Server.Services.Agents;
using Phantom.Server.Services.Events;
using Phantom.Server.Services.Instances;
namespace Phantom.Controller.Services.Rpc;
namespace Phantom.Server.Services.Rpc;
public sealed class MessageToServerListenerFactory {
private readonly ServiceConfiguration configuration;

View File

@ -1,4 +1,4 @@
namespace Phantom.Controller.Services;
namespace Phantom.Server.Services;
public sealed record ServiceConfiguration(
string Version,

View File

@ -0,0 +1,16 @@
using System.Security.Claims;
using Microsoft.AspNetCore.Identity;
namespace Phantom.Server.Services.Users;
public sealed class IdentityLookup {
private readonly UserManager<IdentityUser> userManager;
public IdentityLookup(UserManager<IdentityUser> userManager) {
this.userManager = userManager;
}
public string? GetAuthenticatedUserId(ClaimsPrincipal user) {
return user.Identity is { IsAuthenticated: true } ? userManager.GetUserId(user) : null;
}
}

View File

@ -1,5 +1,5 @@
{
"name": "Phantom.Web.Bootstrap",
"name": "Phantom.Server.Web.Bootstrap",
"lockfileVersion": 2,
"requires": true,
"packages": {

View File

@ -6,6 +6,6 @@
"sass": "^1.55.0"
},
"scripts": {
"compile": "sass --style=compressed ./src/bootstrap.scss ../Phantom.Web/wwwroot/lib/bootstrap/bootstrap.min.css"
"compile": "sass --style=compressed ./src/bootstrap.scss ../Phantom.Server.Web/wwwroot/lib/bootstrap/bootstrap.min.css"
}
}

Some files were not shown because too many files have changed in this diff Show More