diff --git a/.idea/.idea.ProjectLighthouse/.idea/indexLayout.xml b/.idea/.idea.ProjectLighthouse/.idea/indexLayout.xml index 46037895..e9bcd75e 100644 --- a/.idea/.idea.ProjectLighthouse/.idea/indexLayout.xml +++ b/.idea/.idea.ProjectLighthouse/.idea/indexLayout.xml @@ -3,11 +3,13 @@ + .config/dotnet-tools.json .github .gitignore .idea CONTRIBUTING.md DatabaseMigrations + LICENSE ProjectLighthouse.sln.DotSettings ProjectLighthouse.sln.DotSettings.user README.md diff --git a/ProjectLighthouse/Administration/CompletedMigration.cs b/ProjectLighthouse/Administration/CompletedMigration.cs new file mode 100644 index 00000000..3ddd3b25 --- /dev/null +++ b/ProjectLighthouse/Administration/CompletedMigration.cs @@ -0,0 +1,25 @@ +using System; +using System.ComponentModel.DataAnnotations; +using LBPUnion.ProjectLighthouse.Administration.Maintenance; + +namespace LBPUnion.ProjectLighthouse.Administration; + +/// +/// A record of the completion of a . +/// +public class CompletedMigration +{ + /// + /// The name of the migration. + /// + /// + /// Do not use the user-friendly name when setting this. + /// + [Key] + public string MigrationName { get; set; } + + /// + /// The moment the migration was ran. + /// + public DateTime RanAt { get; set; } +} \ No newline at end of file diff --git a/ProjectLighthouse/Administration/Maintenance/IMaintenanceJob.cs b/ProjectLighthouse/Administration/Maintenance/IMaintenanceJob.cs index cf9b82da..15229ed1 100644 --- a/ProjectLighthouse/Administration/Maintenance/IMaintenanceJob.cs +++ b/ProjectLighthouse/Administration/Maintenance/IMaintenanceJob.cs @@ -1,7 +1,9 @@ using System.Threading.Tasks; +using JetBrains.Annotations; namespace LBPUnion.ProjectLighthouse.Administration.Maintenance; +[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)] public interface IMaintenanceJob { public Task Run(); diff --git a/ProjectLighthouse/Administration/Maintenance/IMigrationTask.cs b/ProjectLighthouse/Administration/Maintenance/IMigrationTask.cs new file mode 100644 index 00000000..cba570a3 --- /dev/null +++ b/ProjectLighthouse/Administration/Maintenance/IMigrationTask.cs @@ -0,0 +1,20 @@ +using System.Threading.Tasks; +using JetBrains.Annotations; + +namespace LBPUnion.ProjectLighthouse.Administration.Maintenance; + +[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)] +public interface IMigrationTask +{ + /// + /// The user-friendly name of a migration. + /// + public string Name(); + + /// + /// Performs the migration. + /// + /// The Lighthouse database. + /// True if successful, false if not. + internal Task Run(Database database); +} \ No newline at end of file diff --git a/ProjectLighthouse/Administration/Maintenance/MaintenanceHelper.cs b/ProjectLighthouse/Administration/Maintenance/MaintenanceHelper.cs index 3d86ad38..7d7490d8 100644 --- a/ProjectLighthouse/Administration/Maintenance/MaintenanceHelper.cs +++ b/ProjectLighthouse/Administration/Maintenance/MaintenanceHelper.cs @@ -1,9 +1,11 @@ #nullable enable using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Reflection; using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.Logging.Loggers; @@ -11,24 +13,16 @@ namespace LBPUnion.ProjectLighthouse.Administration.Maintenance; public static class MaintenanceHelper { - static MaintenanceHelper() { Commands = getListOfInterfaceObjects(); MaintenanceJobs = getListOfInterfaceObjects(); + MigrationTasks = getListOfInterfaceObjects(); } + public static List Commands { get; } - public static List MaintenanceJobs { get; } - - private static List getListOfInterfaceObjects() where T : class - { - return Assembly.GetExecutingAssembly() - .GetTypes() - .Where(t => t.GetInterfaces().Contains(typeof(T)) && t.GetConstructor(Type.EmptyTypes) != null) - .Select(t => Activator.CreateInstance(t) as T) - .ToList()!; - } + public static List MigrationTasks { get; } public static async Task> RunCommand(string[] args) { @@ -66,11 +60,57 @@ public static class MaintenanceHelper IMaintenanceJob? job = MaintenanceJobs.FirstOrDefault(j => j.GetType().Name == jobName); if (job == null) throw new ArgumentNullException(); - await RunMaintenanceJob(job); - } - - public static async Task RunMaintenanceJob(IMaintenanceJob job) - { await job.Run(); } + + public static async Task RunMigration(IMigrationTask migrationTask, Database? database = null) + { + database ??= new Database(); + + // Migrations should never be run twice. + Debug.Assert(!await database.CompletedMigrations.Has(m => m.MigrationName == migrationTask.GetType().Name)); + + Logger.Info($"Running migration task {migrationTask.Name()}", LogArea.Database); + + bool success; + Exception? exception = null; + + try + { + success = await migrationTask.Run(database); + } + catch(Exception e) + { + success = false; + exception = e; + } + + if(!success) + { + Logger.Error($"Could not run migration {migrationTask.Name()}", LogArea.Database); + if (exception != null) Logger.Error(exception.ToDetailedException(), LogArea.Database); + + return; + } + + Logger.Success($"Successfully completed migration {migrationTask.Name()}", LogArea.Database); + + CompletedMigration completedMigration = new() + { + MigrationName = migrationTask.GetType().Name, + RanAt = DateTime.Now, + }; + + database.CompletedMigrations.Add(completedMigration); + await database.SaveChangesAsync(); + } + + private static List getListOfInterfaceObjects() where T : class + { + return Assembly.GetExecutingAssembly() + .GetTypes() + .Where(t => t.GetInterfaces().Contains(typeof(T)) && t.GetConstructor(Type.EmptyTypes) != null) + .Select(t => Activator.CreateInstance(t) as T) + .ToList()!; + } } \ No newline at end of file diff --git a/ProjectLighthouse/Administration/Maintenance/MaintenanceJobs/CleanupBrokenPhotosMaintenanceJob.cs b/ProjectLighthouse/Administration/Maintenance/MaintenanceJobs/CleanupBrokenPhotosMaintenanceJob.cs index fdd6f523..b8074025 100644 --- a/ProjectLighthouse/Administration/Maintenance/MaintenanceJobs/CleanupBrokenPhotosMaintenanceJob.cs +++ b/ProjectLighthouse/Administration/Maintenance/MaintenanceJobs/CleanupBrokenPhotosMaintenanceJob.cs @@ -25,7 +25,7 @@ public class CleanupBrokenPhotosMaintenanceJob : IMaintenanceJob bool largeHashIsInvalidFile = false; bool tooManyPhotoSubjects = false; bool duplicatePhotoSubjects = false; - bool takenInTheFuture = true; + bool takenInTheFuture = false; // Checks should generally be ordered in least computationally expensive to most. diff --git a/ProjectLighthouse/Administration/Maintenance/MaintenanceJobs/CleanupXMLInjection.cs b/ProjectLighthouse/Administration/Maintenance/MaintenanceJobs/CleanupXMLInjection.cs deleted file mode 100644 index 91dd40d3..00000000 --- a/ProjectLighthouse/Administration/Maintenance/MaintenanceJobs/CleanupXMLInjection.cs +++ /dev/null @@ -1,36 +0,0 @@ -#nullable enable -using System.Threading.Tasks; -using LBPUnion.ProjectLighthouse.Administration.Reports; -using LBPUnion.ProjectLighthouse.Helpers; -using LBPUnion.ProjectLighthouse.Levels; -using LBPUnion.ProjectLighthouse.PlayerData; -using LBPUnion.ProjectLighthouse.PlayerData.Profiles; -using LBPUnion.ProjectLighthouse.PlayerData.Reviews; - -namespace LBPUnion.ProjectLighthouse.Administration.Maintenance.MaintenanceJobs; - -public class CleanupXmlInjection : IMaintenanceJob -{ - private readonly Database database = new(); - public string Name() => "Sanitize user content"; - public string Description() => "Sanitizes all user-generated strings in levels, reviews, comments, users, and scores to prevent XML injection. Only needs to be run once."; - - public async Task Run() - { - foreach (Slot slot in this.database.Slots) SanitizationHelper.SanitizeStringsInClass(slot); - - foreach (Review review in this.database.Reviews) SanitizationHelper.SanitizeStringsInClass(review); - - foreach (Comment comment in this.database.Comments) SanitizationHelper.SanitizeStringsInClass(comment); - - foreach (Score score in this.database.Scores) SanitizationHelper.SanitizeStringsInClass(score); - - foreach (User user in this.database.Users) SanitizationHelper.SanitizeStringsInClass(user); - - foreach (Photo photo in this.database.Photos) SanitizationHelper.SanitizeStringsInClass(photo); - - foreach (GriefReport report in this.database.Reports) SanitizationHelper.SanitizeStringsInClass(report); - - await this.database.SaveChangesAsync(); - } -} \ No newline at end of file diff --git a/ProjectLighthouse/Administration/Maintenance/MigrationTasks/CleanupXMLInjectionMigration.cs b/ProjectLighthouse/Administration/Maintenance/MigrationTasks/CleanupXMLInjectionMigration.cs new file mode 100644 index 00000000..bb1b71e9 --- /dev/null +++ b/ProjectLighthouse/Administration/Maintenance/MigrationTasks/CleanupXMLInjectionMigration.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Helpers; + +namespace LBPUnion.ProjectLighthouse.Administration.Maintenance.MigrationTasks; + +public class CleanupXmlInjectionMigration : IMigrationTask +{ + public string Name() => "Cleanup XML injections"; + + // Weird, but required. Thanks, hejlsberg. + async Task IMigrationTask.Run(Database database) + { + List objsToBeSanitized = new(); + + // Store all the objects we need to sanitize in a list. + // The alternative here is to loop through every table, but thats a ton of code... + objsToBeSanitized.AddRange(database.Slots); + objsToBeSanitized.AddRange(database.Reviews); + objsToBeSanitized.AddRange(database.Comments); + objsToBeSanitized.AddRange(database.Scores); + objsToBeSanitized.AddRange(database.Users); + objsToBeSanitized.AddRange(database.Photos); + objsToBeSanitized.AddRange(database.Reports); + + foreach (object obj in objsToBeSanitized) SanitizationHelper.SanitizeStringsInClass(obj); + + await database.SaveChangesAsync(); + return true; + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Database.cs b/ProjectLighthouse/Database.cs index 7430e070..eca4a621 100644 --- a/ProjectLighthouse/Database.cs +++ b/ProjectLighthouse/Database.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Administration; using LBPUnion.ProjectLighthouse.Administration.Reports; using LBPUnion.ProjectLighthouse.Configuration; using LBPUnion.ProjectLighthouse.Helpers; @@ -21,6 +22,7 @@ namespace LBPUnion.ProjectLighthouse; public class Database : DbContext { + public DbSet CompletedMigrations { get; set; } public DbSet Users { get; set; } public DbSet Locations { get; set; } public DbSet Slots { get; set; } diff --git a/ProjectLighthouse/Extensions/DatabaseExtensions.cs b/ProjectLighthouse/Extensions/DatabaseExtensions.cs index eb8af6c7..a7e27782 100644 --- a/ProjectLighthouse/Extensions/DatabaseExtensions.cs +++ b/ProjectLighthouse/Extensions/DatabaseExtensions.cs @@ -1,4 +1,7 @@ +using System; using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; using LBPUnion.ProjectLighthouse.Levels; using LBPUnion.ProjectLighthouse.PlayerData; using LBPUnion.ProjectLighthouse.PlayerData.Reviews; @@ -52,4 +55,7 @@ public static class DatabaseExtensions return query; } + + public static async Task Has(this IQueryable queryable, Expression> predicate) => + await queryable.FirstOrDefaultAsync(predicate) != null; } \ No newline at end of file diff --git a/ProjectLighthouse/Migrations/20220610061641_AddCompletedMigrations.cs b/ProjectLighthouse/Migrations/20220610061641_AddCompletedMigrations.cs new file mode 100644 index 00000000..5c33f89a --- /dev/null +++ b/ProjectLighthouse/Migrations/20220610061641_AddCompletedMigrations.cs @@ -0,0 +1,37 @@ +using System; +using LBPUnion.ProjectLighthouse; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ProjectLighthouse.Migrations +{ + [DbContext(typeof(Database))] + [Migration("20220610061641_AddCompletedMigrations")] + public class AddCompletedMigrations : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "CompletedMigrations", + columns: table => new + { + MigrationName = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + RanAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_CompletedMigrations", x => x.MigrationName); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "CompletedMigrations"); + } + } +} diff --git a/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs b/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs index 6acb3ff5..147d7e03 100644 --- a/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs +++ b/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs @@ -1,4 +1,5 @@ // +using System; using LBPUnion.ProjectLighthouse; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; @@ -15,35 +16,69 @@ namespace ProjectLighthouse.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "6.0.4") + .HasAnnotation("ProductVersion", "6.0.5") .HasAnnotation("Relational:MaxIdentifierLength", 64); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.AuthenticationAttempt", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Administration.CompletedMigration", b => { - b.Property("AuthenticationAttemptId") + b.Property("MigrationName") + .HasColumnType("varchar(255)"); + + b.Property("RanAt") + .HasColumnType("datetime(6)"); + + b.HasKey("MigrationName"); + + b.ToTable("CompletedMigrations"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Administration.Reports.GriefReport", b => + { + b.Property("ReportId") .ValueGeneratedOnAdd() .HasColumnType("int"); - b.Property("GameTokenId") - .HasColumnType("int"); - - b.Property("IPAddress") + b.Property("Bounds") .HasColumnType("longtext"); - b.Property("Platform") + b.Property("GriefStateHash") + .HasColumnType("longtext"); + + b.Property("InitialStateHash") + .HasColumnType("longtext"); + + b.Property("JpegHash") + .HasColumnType("longtext"); + + b.Property("LevelId") + .HasColumnType("int"); + + b.Property("LevelOwner") + .HasColumnType("longtext"); + + b.Property("LevelType") + .HasColumnType("longtext"); + + b.Property("Players") + .HasColumnType("longtext"); + + b.Property("ReportingPlayerId") .HasColumnType("int"); b.Property("Timestamp") .HasColumnType("bigint"); - b.HasKey("AuthenticationAttemptId"); + b.Property("Type") + .HasColumnType("int"); - b.HasIndex("GameTokenId"); + b.HasKey("ReportId"); - b.ToTable("AuthenticationAttempts"); + b.HasIndex("ReportingPlayerId"); + + b.ToTable("Reports"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Categories.DatabaseCategory", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Levels.Categories.DatabaseCategory", b => { b.Property("CategoryId") .ValueGeneratedOnAdd() @@ -69,62 +104,7 @@ namespace ProjectLighthouse.Migrations b.ToTable("CustomCategories"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b => - { - b.Property("TokenId") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("Approved") - .HasColumnType("tinyint(1)"); - - b.Property("GameVersion") - .HasColumnType("int"); - - b.Property("Platform") - .HasColumnType("int"); - - b.Property("Used") - .HasColumnType("tinyint(1)"); - - b.Property("UserId") - .HasColumnType("int"); - - b.Property("UserLocation") - .HasColumnType("longtext"); - - b.Property("UserToken") - .HasColumnType("longtext"); - - b.HasKey("TokenId"); - - b.HasIndex("UserId"); - - b.ToTable("GameTokens"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.HeartedProfile", b => - { - b.Property("HeartedProfileId") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("HeartedUserId") - .HasColumnType("int"); - - b.Property("UserId") - .HasColumnType("int"); - - b.HasKey("HeartedProfileId"); - - b.HasIndex("HeartedUserId"); - - b.HasIndex("UserId"); - - b.ToTable("HeartedProfiles"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.HeartedLevel", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Levels.HeartedLevel", b => { b.Property("HeartedLevelId") .ValueGeneratedOnAdd() @@ -145,7 +125,7 @@ namespace ProjectLighthouse.Migrations b.ToTable("HeartedLevels"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.QueuedLevel", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Levels.QueuedLevel", b => { b.Property("QueuedLevelId") .ValueGeneratedOnAdd() @@ -166,7 +146,7 @@ namespace ProjectLighthouse.Migrations b.ToTable("QueuedLevels"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.RatedLevel", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Levels.RatedLevel", b => { b.Property("RatedLevelId") .ValueGeneratedOnAdd() @@ -193,7 +173,7 @@ namespace ProjectLighthouse.Migrations b.ToTable("RatedLevels"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.Slot", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Levels.Slot", b => { b.Property("SlotId") .ValueGeneratedOnAdd() @@ -318,7 +298,7 @@ namespace ProjectLighthouse.Migrations b.ToTable("Slots"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.VisitedLevel", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Levels.VisitedLevel", b => { b.Property("VisitedLevelId") .ValueGeneratedOnAdd() @@ -351,7 +331,66 @@ namespace ProjectLighthouse.Migrations b.ToTable("VisitedLevels"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Photo", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.AuthenticationAttempt", b => + { + b.Property("AuthenticationAttemptId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("GameTokenId") + .HasColumnType("int"); + + b.Property("IPAddress") + .HasColumnType("longtext"); + + b.Property("Platform") + .HasColumnType("int"); + + b.Property("Timestamp") + .HasColumnType("bigint"); + + b.HasKey("AuthenticationAttemptId"); + + b.HasIndex("GameTokenId"); + + b.ToTable("AuthenticationAttempts"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.GameToken", b => + { + b.Property("TokenId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Approved") + .HasColumnType("tinyint(1)"); + + b.Property("GameVersion") + .HasColumnType("int"); + + b.Property("Platform") + .HasColumnType("int"); + + b.Property("Used") + .HasColumnType("tinyint(1)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("UserLocation") + .HasColumnType("longtext"); + + b.Property("UserToken") + .HasColumnType("longtext"); + + b.HasKey("TokenId"); + + b.HasIndex("UserId"); + + b.ToTable("GameTokens"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Photo", b => { b.Property("PhotoId") .ValueGeneratedOnAdd() @@ -390,7 +429,7 @@ namespace ProjectLighthouse.Migrations b.ToTable("Photos"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.PhotoSubject", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.PhotoSubject", b => { b.Property("PhotoSubjectId") .ValueGeneratedOnAdd() @@ -409,7 +448,7 @@ namespace ProjectLighthouse.Migrations b.ToTable("PhotoSubjects"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Comment", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Profiles.Comment", b => { b.Property("CommentId") .ValueGeneratedOnAdd() @@ -452,7 +491,7 @@ namespace ProjectLighthouse.Migrations b.ToTable("Comments"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Email.EmailSetToken", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Profiles.Email.EmailSetToken", b => { b.Property("EmailSetTokenId") .ValueGeneratedOnAdd() @@ -471,7 +510,7 @@ namespace ProjectLighthouse.Migrations b.ToTable("EmailSetTokens"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Email.EmailVerificationToken", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Profiles.Email.EmailVerificationToken", b => { b.Property("EmailVerificationTokenId") .ValueGeneratedOnAdd() @@ -490,7 +529,28 @@ namespace ProjectLighthouse.Migrations b.ToTable("EmailVerificationTokens"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.LastContact", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Profiles.HeartedProfile", b => + { + b.Property("HeartedProfileId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("HeartedUserId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("HeartedProfileId"); + + b.HasIndex("HeartedUserId"); + + b.HasIndex("UserId"); + + b.ToTable("HeartedProfiles"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Profiles.LastContact", b => { b.Property("UserId") .HasColumnType("int"); @@ -509,7 +569,7 @@ namespace ProjectLighthouse.Migrations b.ToTable("LastContacts"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Location", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Profiles.Location", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -526,169 +586,7 @@ namespace ProjectLighthouse.Migrations b.ToTable("Locations"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Reaction", b => - { - b.Property("RatingId") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("Rating") - .HasColumnType("int"); - - b.Property("TargetId") - .HasColumnType("int"); - - b.Property("UserId") - .HasColumnType("int"); - - b.HasKey("RatingId"); - - b.ToTable("Reactions"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Reports.GriefReport", b => - { - b.Property("ReportId") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("Bounds") - .HasColumnType("longtext"); - - b.Property("GriefStateHash") - .HasColumnType("longtext"); - - b.Property("InitialStateHash") - .HasColumnType("longtext"); - - b.Property("JpegHash") - .HasColumnType("longtext"); - - b.Property("LevelId") - .HasColumnType("int"); - - b.Property("LevelOwner") - .HasColumnType("longtext"); - - b.Property("LevelType") - .HasColumnType("longtext"); - - b.Property("Players") - .HasColumnType("longtext"); - - b.Property("ReportingPlayerId") - .HasColumnType("int"); - - b.Property("Timestamp") - .HasColumnType("bigint"); - - b.Property("Type") - .HasColumnType("int"); - - b.HasKey("ReportId"); - - b.HasIndex("ReportingPlayerId"); - - b.ToTable("Reports"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Reviews.RatedReview", b => - { - b.Property("RatedReviewId") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("ReviewId") - .HasColumnType("int"); - - b.Property("Thumb") - .HasColumnType("int"); - - b.Property("UserId") - .HasColumnType("int"); - - b.HasKey("RatedReviewId"); - - b.HasIndex("ReviewId"); - - b.HasIndex("UserId"); - - b.ToTable("RatedReviews"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Reviews.Review", b => - { - b.Property("ReviewId") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("Deleted") - .HasColumnType("tinyint(1)"); - - b.Property("DeletedBy") - .HasColumnType("int"); - - b.Property("LabelCollection") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("ReviewerId") - .HasColumnType("int"); - - b.Property("SlotId") - .HasColumnType("int"); - - b.Property("Text") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("Thumb") - .HasColumnType("int"); - - b.Property("ThumbsDown") - .HasColumnType("int"); - - b.Property("ThumbsUp") - .HasColumnType("int"); - - b.Property("Timestamp") - .HasColumnType("bigint"); - - b.HasKey("ReviewId"); - - b.HasIndex("ReviewerId"); - - b.HasIndex("SlotId"); - - b.ToTable("Reviews"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Score", b => - { - b.Property("ScoreId") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("PlayerIdCollection") - .HasColumnType("longtext"); - - b.Property("Points") - .HasColumnType("int"); - - b.Property("SlotId") - .HasColumnType("int"); - - b.Property("Type") - .HasColumnType("int"); - - b.HasKey("ScoreId"); - - b.HasIndex("SlotId"); - - b.ToTable("Scores"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", b => { b.Property("UserId") .ValueGeneratedOnAdd() @@ -761,7 +659,7 @@ namespace ProjectLighthouse.Migrations b.ToTable("Users"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.UserApprovedIpAddress", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Profiles.UserApprovedIpAddress", b => { b.Property("UserApprovedIpAddressId") .ValueGeneratedOnAdd() @@ -780,7 +678,123 @@ namespace ProjectLighthouse.Migrations b.ToTable("UserApprovedIpAddresses"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.WebToken", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Reaction", b => + { + b.Property("RatingId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Rating") + .HasColumnType("int"); + + b.Property("TargetId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("RatingId"); + + b.ToTable("Reactions"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Reviews.RatedReview", b => + { + b.Property("RatedReviewId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ReviewId") + .HasColumnType("int"); + + b.Property("Thumb") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("RatedReviewId"); + + b.HasIndex("ReviewId"); + + b.HasIndex("UserId"); + + b.ToTable("RatedReviews"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Reviews.Review", b => + { + b.Property("ReviewId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Deleted") + .HasColumnType("tinyint(1)"); + + b.Property("DeletedBy") + .HasColumnType("int"); + + b.Property("LabelCollection") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ReviewerId") + .HasColumnType("int"); + + b.Property("SlotId") + .HasColumnType("int"); + + b.Property("Text") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Thumb") + .HasColumnType("int"); + + b.Property("ThumbsDown") + .HasColumnType("int"); + + b.Property("ThumbsUp") + .HasColumnType("int"); + + b.Property("Timestamp") + .HasColumnType("bigint"); + + b.HasKey("ReviewId"); + + b.HasIndex("ReviewerId"); + + b.HasIndex("SlotId"); + + b.ToTable("Reviews"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Score", b => + { + b.Property("ScoreId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("PlayerIdCollection") + .HasColumnType("longtext"); + + b.Property("Points") + .HasColumnType("int"); + + b.Property("SlotId") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("ScoreId"); + + b.HasIndex("SlotId"); + + b.ToTable("Scores"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.WebToken", b => { b.Property("TokenId") .ValueGeneratedOnAdd() @@ -797,9 +811,115 @@ namespace ProjectLighthouse.Migrations b.ToTable("WebTokens"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.AuthenticationAttempt", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Administration.Reports.GriefReport", b => { - b.HasOne("LBPUnion.ProjectLighthouse.Types.GameToken", "GameToken") + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "ReportingPlayer") + .WithMany() + .HasForeignKey("ReportingPlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ReportingPlayer"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Levels.HeartedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Levels.QueuedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Levels.RatedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Levels.Slot", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "Creator") + .WithMany() + .HasForeignKey("CreatorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Location"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Levels.VisitedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.AuthenticationAttempt", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.GameToken", "GameToken") .WithMany() .HasForeignKey("GameTokenId") .OnDelete(DeleteBehavior.Cascade) @@ -808,9 +928,9 @@ namespace ProjectLighthouse.Migrations b.Navigation("GameToken"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.GameToken", b => { - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "User") .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) @@ -819,15 +939,70 @@ namespace ProjectLighthouse.Migrations b.Navigation("User"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.HeartedProfile", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Photo", b => { - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "HeartedUser") + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "Creator") + .WithMany() + .HasForeignKey("CreatorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.PhotoSubject", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Profiles.Comment", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "Poster") + .WithMany() + .HasForeignKey("PosterUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Poster"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Profiles.Email.EmailSetToken", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Profiles.Email.EmailVerificationToken", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Profiles.HeartedProfile", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "HeartedUser") .WithMany() .HasForeignKey("HeartedUserId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "User") .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) @@ -838,115 +1013,31 @@ namespace ProjectLighthouse.Migrations b.Navigation("User"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.HeartedLevel", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Profiles.LastContact", b => { - b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") - .WithMany() - .HasForeignKey("SlotId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "User") .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.Navigation("Slot"); - b.Navigation("User"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.QueuedLevel", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", b => { - b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") - .WithMany() - .HasForeignKey("SlotId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Slot"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.RatedLevel", b => - { - b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") - .WithMany() - .HasForeignKey("SlotId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Slot"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.Slot", b => - { - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Creator") - .WithMany() - .HasForeignKey("CreatorId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("LBPUnion.ProjectLighthouse.Types.Profiles.Location", "Location") + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.Location", "Location") .WithMany() .HasForeignKey("LocationId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.Navigation("Creator"); - b.Navigation("Location"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.VisitedLevel", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Profiles.UserApprovedIpAddress", b => { - b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") - .WithMany() - .HasForeignKey("SlotId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Slot"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Photo", b => - { - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Creator") - .WithMany() - .HasForeignKey("CreatorId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Creator"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.PhotoSubject", b => - { - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "User") .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) @@ -955,70 +1046,15 @@ namespace ProjectLighthouse.Migrations b.Navigation("User"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Comment", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Reviews.RatedReview", b => { - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Poster") - .WithMany() - .HasForeignKey("PosterUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Poster"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Email.EmailSetToken", b => - { - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("User"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Email.EmailVerificationToken", b => - { - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("User"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.LastContact", b => - { - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("User"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Reports.GriefReport", b => - { - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "ReportingPlayer") - .WithMany() - .HasForeignKey("ReportingPlayerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ReportingPlayer"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Reviews.RatedReview", b => - { - b.HasOne("LBPUnion.ProjectLighthouse.Types.Reviews.Review", "Review") + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Reviews.Review", "Review") .WithMany() .HasForeignKey("ReviewId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "User") .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) @@ -1029,15 +1065,15 @@ namespace ProjectLighthouse.Migrations b.Navigation("User"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Reviews.Review", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Reviews.Review", b => { - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Reviewer") + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "Reviewer") .WithMany() .HasForeignKey("ReviewerId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + b.HasOne("LBPUnion.ProjectLighthouse.Levels.Slot", "Slot") .WithMany() .HasForeignKey("SlotId") .OnDelete(DeleteBehavior.Cascade) @@ -1048,9 +1084,9 @@ namespace ProjectLighthouse.Migrations b.Navigation("Slot"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Score", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Score", b => { - b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + b.HasOne("LBPUnion.ProjectLighthouse.Levels.Slot", "Slot") .WithMany() .HasForeignKey("SlotId") .OnDelete(DeleteBehavior.Cascade) @@ -1058,28 +1094,6 @@ namespace ProjectLighthouse.Migrations b.Navigation("Slot"); }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b => - { - b.HasOne("LBPUnion.ProjectLighthouse.Types.Profiles.Location", "Location") - .WithMany() - .HasForeignKey("LocationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Location"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.UserApprovedIpAddress", b => - { - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("User"); - }); #pragma warning restore 612, 618 } } diff --git a/ProjectLighthouse/ProjectLighthouse.csproj b/ProjectLighthouse/ProjectLighthouse.csproj index f38c51ff..be8a30f9 100644 --- a/ProjectLighthouse/ProjectLighthouse.csproj +++ b/ProjectLighthouse/ProjectLighthouse.csproj @@ -50,6 +50,10 @@ + + + + diff --git a/ProjectLighthouse/StartupTasks.cs b/ProjectLighthouse/StartupTasks.cs index caff91bc..234cf75e 100644 --- a/ProjectLighthouse/StartupTasks.cs +++ b/ProjectLighthouse/StartupTasks.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; +using LBPUnion.ProjectLighthouse.Administration; using LBPUnion.ProjectLighthouse.Administration.Maintenance; using LBPUnion.ProjectLighthouse.Configuration; using LBPUnion.ProjectLighthouse.Extensions; @@ -112,14 +114,35 @@ public static class StartupTasks Logger.Success($"Ready! Startup took {stopwatch.ElapsedMilliseconds}ms. Passing off control to ASP.NET...", LogArea.Startup); } - private static void migrateDatabase(DbContext database) + private static void migrateDatabase(Database database) { + Stopwatch totalStopwatch = new(); Stopwatch stopwatch = new(); + totalStopwatch.Start(); stopwatch.Start(); database.Database.MigrateAsync().Wait(); + stopwatch.Stop(); + Logger.Success($"Structure migration took {stopwatch.ElapsedMilliseconds}ms.", LogArea.Database); + + stopwatch.Reset(); + stopwatch.Start(); + + List completedMigrations = database.CompletedMigrations.ToList(); + List migrationsToRun = MaintenanceHelper.MigrationTasks + .Where(migrationTask => !completedMigrations + .Select(m => m.MigrationName) + .Contains(migrationTask.GetType().Name) + ).ToList(); + + foreach (IMigrationTask migrationTask in migrationsToRun) + { + MaintenanceHelper.RunMigration(migrationTask, database).Wait(); + } stopwatch.Stop(); - Logger.Success($"Migration took {stopwatch.ElapsedMilliseconds}ms.", LogArea.Database); + totalStopwatch.Stop(); + Logger.Success($"Extra migration tasks took {stopwatch.ElapsedMilliseconds}ms.", LogArea.Database); + Logger.Success($"Total migration took {totalStopwatch.ElapsedMilliseconds}ms.", LogArea.Database); } } \ No newline at end of file diff --git a/scripts-and-tools/create-migration.sh b/scripts-and-tools/create-migration.sh index 5ccbe0fb..43c6a3d2 100755 --- a/scripts-and-tools/create-migration.sh +++ b/scripts-and-tools/create-migration.sh @@ -1,4 +1,9 @@ #!/bin/bash +# Developer script to create EntityFramework database migrations +# +# $1: Name of the migration, e.g. SwitchToPermissionLevels +# Invoked manually + export LIGHTHOUSE_DB_CONNECTION_STRING='server=127.0.0.1;uid=root;pwd=lighthouse;database=lighthouse' dotnet ef migrations add "$1" --project ProjectLighthouse \ No newline at end of file