diff --git a/src/Tgstation.Server.Host/Database/Migrations/20250102194845_SLNormalizeSqlite.Designer.cs b/src/Tgstation.Server.Host/Database/Migrations/20250102194845_SLNormalizeSqlite.Designer.cs
new file mode 100644
index 0000000000..a765985528
--- /dev/null
+++ b/src/Tgstation.Server.Host/Database/Migrations/20250102194845_SLNormalizeSqlite.Designer.cs
@@ -0,0 +1,1038 @@
+//
+using System;
+
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+using Tgstation.Server.Host.Database;
+
+#nullable disable
+
+namespace Tgstation.Server.Host.Migrations.SqliteDatabase
+{
+ [DbContext(typeof(SqliteDatabaseContext))]
+ [Migration("20250102194845_SLNormalizeSqlite")]
+ partial class SLNormalizeSqlite
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder.HasAnnotation("ProductVersion", "9.0.0");
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("ChannelLimit")
+ .HasColumnType("INTEGER");
+
+ b.Property("ConnectionString")
+ .IsRequired()
+ .HasMaxLength(10000)
+ .HasColumnType("TEXT");
+
+ b.Property("Enabled")
+ .HasColumnType("INTEGER");
+
+ b.Property("InstanceId")
+ .HasColumnType("INTEGER");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("Provider")
+ .HasColumnType("INTEGER");
+
+ b.Property("ReconnectionInterval")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("InstanceId", "Name")
+ .IsUnique();
+
+ b.ToTable("ChatBots");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("ChatSettingsId")
+ .HasColumnType("INTEGER");
+
+ b.Property("DiscordChannelId")
+ .HasColumnType("INTEGER");
+
+ b.Property("IrcChannel")
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("IsAdminChannel")
+ .HasColumnType("INTEGER");
+
+ b.Property("IsSystemChannel")
+ .HasColumnType("INTEGER");
+
+ b.Property("IsUpdatesChannel")
+ .HasColumnType("INTEGER");
+
+ b.Property("IsWatchdogChannel")
+ .HasColumnType("INTEGER");
+
+ b.Property("Tag")
+ .HasMaxLength(10000)
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ChatSettingsId", "DiscordChannelId")
+ .IsUnique();
+
+ b.HasIndex("ChatSettingsId", "IrcChannel")
+ .IsUnique();
+
+ b.ToTable("ChatChannels");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("DMApiMajorVersion")
+ .HasColumnType("INTEGER");
+
+ b.Property("DMApiMinorVersion")
+ .HasColumnType("INTEGER");
+
+ b.Property("DMApiPatchVersion")
+ .HasColumnType("INTEGER");
+
+ b.Property("DirectoryName")
+ .HasColumnType("TEXT");
+
+ b.Property("DmeName")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("EngineVersion")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("GitHubDeploymentId")
+ .HasColumnType("INTEGER");
+
+ b.Property("GitHubRepoId")
+ .HasColumnType("INTEGER");
+
+ b.Property("JobId")
+ .HasColumnType("INTEGER");
+
+ b.Property("MinimumSecurityLevel")
+ .HasColumnType("INTEGER");
+
+ b.Property("Output")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("RepositoryOrigin")
+ .HasColumnType("TEXT");
+
+ b.Property("RevisionInformationId")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("DirectoryName");
+
+ b.HasIndex("JobId")
+ .IsUnique();
+
+ b.HasIndex("RevisionInformationId");
+
+ b.ToTable("CompileJobs");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("AdditionalParameters")
+ .IsRequired()
+ .HasMaxLength(10000)
+ .HasColumnType("TEXT");
+
+ b.Property("AllowWebClient")
+ .HasColumnType("INTEGER");
+
+ b.Property("AutoStart")
+ .HasColumnType("INTEGER");
+
+ b.Property("DumpOnHealthCheckRestart")
+ .HasColumnType("INTEGER");
+
+ b.Property("HealthCheckSeconds")
+ .HasColumnType("INTEGER");
+
+ b.Property("InstanceId")
+ .HasColumnType("INTEGER");
+
+ b.Property("LogOutput")
+ .HasColumnType("INTEGER");
+
+ b.Property("MapThreads")
+ .HasColumnType("INTEGER");
+
+ b.Property("Minidumps")
+ .HasColumnType("INTEGER");
+
+ b.Property("OpenDreamTopicPort")
+ .HasColumnType("INTEGER");
+
+ b.Property("Port")
+ .HasColumnType("INTEGER");
+
+ b.Property("SecurityLevel")
+ .HasColumnType("INTEGER");
+
+ b.Property("StartProfiler")
+ .HasColumnType("INTEGER");
+
+ b.Property("StartupTimeout")
+ .HasColumnType("INTEGER");
+
+ b.Property("TopicRequestTimeout")
+ .HasColumnType("INTEGER");
+
+ b.Property("Visibility")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("InstanceId")
+ .IsUnique();
+
+ b.ToTable("DreamDaemonSettings");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("ApiValidationPort")
+ .HasColumnType("INTEGER");
+
+ b.Property("ApiValidationSecurityLevel")
+ .HasColumnType("INTEGER");
+
+ b.Property("CompilerAdditionalArguments")
+ .HasMaxLength(10000)
+ .HasColumnType("TEXT");
+
+ b.Property("DMApiValidationMode")
+ .HasColumnType("INTEGER");
+
+ b.Property("InstanceId")
+ .HasColumnType("INTEGER");
+
+ b.Property("ProjectName")
+ .HasMaxLength(10000)
+ .HasColumnType("TEXT");
+
+ b.Property("Timeout")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("InstanceId")
+ .IsUnique();
+
+ b.ToTable("DreamMakerSettings");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("AutoStartCron")
+ .IsRequired()
+ .HasMaxLength(1000)
+ .HasColumnType("TEXT");
+
+ b.Property("AutoStopCron")
+ .IsRequired()
+ .HasMaxLength(1000)
+ .HasColumnType("TEXT");
+
+ b.Property("AutoUpdateCron")
+ .IsRequired()
+ .HasMaxLength(1000)
+ .HasColumnType("TEXT");
+
+ b.Property("AutoUpdateInterval")
+ .HasColumnType("INTEGER");
+
+ b.Property("ChatBotLimit")
+ .HasColumnType("INTEGER");
+
+ b.Property("ConfigurationType")
+ .HasColumnType("INTEGER");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("Online")
+ .HasColumnType("INTEGER");
+
+ b.Property("Path")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("SwarmIdentifer")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Path", "SwarmIdentifer")
+ .IsUnique();
+
+ b.ToTable("Instances");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.InstancePermissionSet", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("ChatBotRights")
+ .HasColumnType("INTEGER");
+
+ b.Property("ConfigurationRights")
+ .HasColumnType("INTEGER");
+
+ b.Property("DreamDaemonRights")
+ .HasColumnType("INTEGER");
+
+ b.Property("DreamMakerRights")
+ .HasColumnType("INTEGER");
+
+ b.Property("EngineRights")
+ .HasColumnType("INTEGER");
+
+ b.Property("InstanceId")
+ .HasColumnType("INTEGER");
+
+ b.Property("InstancePermissionSetRights")
+ .HasColumnType("INTEGER");
+
+ b.Property("PermissionSetId")
+ .HasColumnType("INTEGER");
+
+ b.Property("RepositoryRights")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("InstanceId");
+
+ b.HasIndex("PermissionSetId", "InstanceId")
+ .IsUnique();
+
+ b.ToTable("InstancePermissionSets");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("CancelRight")
+ .HasColumnType("INTEGER");
+
+ b.Property("CancelRightsType")
+ .HasColumnType("INTEGER");
+
+ b.Property("Cancelled")
+ .HasColumnType("INTEGER");
+
+ b.Property("CancelledById")
+ .HasColumnType("INTEGER");
+
+ b.Property("Description")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("ErrorCode")
+ .HasColumnType("INTEGER");
+
+ b.Property("ExceptionDetails")
+ .HasColumnType("TEXT");
+
+ b.Property("InstanceId")
+ .HasColumnType("INTEGER");
+
+ b.Property("JobCode")
+ .HasColumnType("INTEGER");
+
+ b.Property("StartedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("StartedById")
+ .HasColumnType("INTEGER");
+
+ b.Property("StoppedAt")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CancelledById");
+
+ b.HasIndex("InstanceId");
+
+ b.HasIndex("StartedById");
+
+ b.ToTable("Jobs");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("ExternalUserId")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("Provider")
+ .HasColumnType("INTEGER");
+
+ b.Property("UserId")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId");
+
+ b.HasIndex("Provider", "ExternalUserId")
+ .IsUnique();
+
+ b.ToTable("OAuthConnections");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.PermissionSet", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("AdministrationRights")
+ .HasColumnType("INTEGER");
+
+ b.Property("GroupId")
+ .HasColumnType("INTEGER");
+
+ b.Property("InstanceManagerRights")
+ .HasColumnType("INTEGER");
+
+ b.Property("UserId")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("GroupId")
+ .IsUnique();
+
+ b.HasIndex("UserId")
+ .IsUnique();
+
+ b.ToTable("PermissionSets");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("AccessIdentifier")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("CompileJobId")
+ .HasColumnType("INTEGER");
+
+ b.Property("InitialCompileJobId")
+ .HasColumnType("INTEGER");
+
+ b.Property("LaunchSecurityLevel")
+ .HasColumnType("INTEGER");
+
+ b.Property("LaunchVisibility")
+ .HasColumnType("INTEGER");
+
+ b.Property("Port")
+ .HasColumnType("INTEGER");
+
+ b.Property("ProcessId")
+ .HasColumnType("INTEGER");
+
+ b.Property("RebootState")
+ .HasColumnType("INTEGER");
+
+ b.Property("TopicPort")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CompileJobId");
+
+ b.HasIndex("InitialCompileJobId");
+
+ b.ToTable("ReattachInformations");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("AccessToken")
+ .HasMaxLength(10000)
+ .HasColumnType("TEXT");
+
+ b.Property("AccessUser")
+ .HasMaxLength(10000)
+ .HasColumnType("TEXT");
+
+ b.Property("AutoUpdatesKeepTestMerges")
+ .HasColumnType("INTEGER");
+
+ b.Property("AutoUpdatesSynchronize")
+ .HasColumnType("INTEGER");
+
+ b.Property("CommitterEmail")
+ .IsRequired()
+ .HasMaxLength(10000)
+ .HasColumnType("TEXT");
+
+ b.Property("CommitterName")
+ .IsRequired()
+ .HasMaxLength(10000)
+ .HasColumnType("TEXT");
+
+ b.Property("CreateGitHubDeployments")
+ .HasColumnType("INTEGER");
+
+ b.Property("InstanceId")
+ .HasColumnType("INTEGER");
+
+ b.Property("PostTestMergeComment")
+ .HasColumnType("INTEGER");
+
+ b.Property("PushTestMergeCommits")
+ .HasColumnType("INTEGER");
+
+ b.Property("ShowTestMergeCommitters")
+ .HasColumnType("INTEGER");
+
+ b.Property("UpdateSubmodules")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("InstanceId")
+ .IsUnique();
+
+ b.ToTable("RepositorySettings");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("RevisionInformationId")
+ .HasColumnType("INTEGER");
+
+ b.Property("TestMergeId")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("RevisionInformationId");
+
+ b.HasIndex("TestMergeId");
+
+ b.ToTable("RevInfoTestMerges");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("CommitSha")
+ .IsRequired()
+ .HasMaxLength(40)
+ .HasColumnType("TEXT");
+
+ b.Property("InstanceId")
+ .HasColumnType("INTEGER");
+
+ b.Property("OriginCommitSha")
+ .IsRequired()
+ .HasMaxLength(40)
+ .HasColumnType("TEXT");
+
+ b.Property("Timestamp")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("InstanceId", "CommitSha")
+ .IsUnique();
+
+ b.ToTable("RevisionInformations");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("Author")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("BodyAtMerge")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("Comment")
+ .HasMaxLength(10000)
+ .HasColumnType("TEXT");
+
+ b.Property("MergedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("MergedById")
+ .HasColumnType("INTEGER");
+
+ b.Property("Number")
+ .HasColumnType("INTEGER");
+
+ b.Property("PrimaryRevisionInformationId")
+ .HasColumnType("INTEGER");
+
+ b.Property("TargetCommitSha")
+ .IsRequired()
+ .HasMaxLength(40)
+ .HasColumnType("TEXT");
+
+ b.Property("TitleAtMerge")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("Url")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("MergedById");
+
+ b.HasIndex("PrimaryRevisionInformationId")
+ .IsUnique();
+
+ b.ToTable("TestMerges");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("CanonicalName")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedById")
+ .HasColumnType("INTEGER");
+
+ b.Property("Enabled")
+ .HasColumnType("INTEGER");
+
+ b.Property("GroupId")
+ .HasColumnType("INTEGER");
+
+ b.Property("LastPasswordUpdate")
+ .HasColumnType("TEXT");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("PasswordHash")
+ .HasColumnType("TEXT");
+
+ b.Property("SystemIdentifier")
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CanonicalName")
+ .IsUnique();
+
+ b.HasIndex("CreatedById");
+
+ b.HasIndex("GroupId");
+
+ b.HasIndex("SystemIdentifier")
+ .IsUnique();
+
+ b.ToTable("Users");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.UserGroup", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Name")
+ .IsUnique();
+
+ b.ToTable("Groups");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
+ {
+ b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
+ .WithMany("ChatSettings")
+ .HasForeignKey("InstanceId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Instance");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
+ {
+ b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings")
+ .WithMany("Channels")
+ .HasForeignKey("ChatSettingsId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("ChatSettings");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
+ {
+ b.HasOne("Tgstation.Server.Host.Models.Job", "Job")
+ .WithOne()
+ .HasForeignKey("Tgstation.Server.Host.Models.CompileJob", "JobId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
+ .WithMany("CompileJobs")
+ .HasForeignKey("RevisionInformationId")
+ .OnDelete(DeleteBehavior.ClientNoAction)
+ .IsRequired();
+
+ b.Navigation("Job");
+
+ b.Navigation("RevisionInformation");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
+ {
+ b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
+ .WithOne("DreamDaemonSettings")
+ .HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Instance");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
+ {
+ b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
+ .WithOne("DreamMakerSettings")
+ .HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Instance");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.InstancePermissionSet", b =>
+ {
+ b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
+ .WithMany("InstancePermissionSets")
+ .HasForeignKey("InstanceId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Tgstation.Server.Host.Models.PermissionSet", "PermissionSet")
+ .WithMany("InstancePermissionSets")
+ .HasForeignKey("PermissionSetId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Instance");
+
+ b.Navigation("PermissionSet");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
+ {
+ b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy")
+ .WithMany()
+ .HasForeignKey("CancelledById");
+
+ b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
+ .WithMany("Jobs")
+ .HasForeignKey("InstanceId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy")
+ .WithMany()
+ .HasForeignKey("StartedById")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("CancelledBy");
+
+ b.Navigation("Instance");
+
+ b.Navigation("StartedBy");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b =>
+ {
+ b.HasOne("Tgstation.Server.Host.Models.User", "User")
+ .WithMany("OAuthConnections")
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("User");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.PermissionSet", b =>
+ {
+ b.HasOne("Tgstation.Server.Host.Models.UserGroup", "Group")
+ .WithOne("PermissionSet")
+ .HasForeignKey("Tgstation.Server.Host.Models.PermissionSet", "GroupId")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("Tgstation.Server.Host.Models.User", "User")
+ .WithOne("PermissionSet")
+ .HasForeignKey("Tgstation.Server.Host.Models.PermissionSet", "UserId")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.Navigation("Group");
+
+ b.Navigation("User");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
+ {
+ b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
+ .WithMany()
+ .HasForeignKey("CompileJobId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Tgstation.Server.Host.Models.CompileJob", "InitialCompileJob")
+ .WithMany()
+ .HasForeignKey("InitialCompileJobId");
+
+ b.Navigation("CompileJob");
+
+ b.Navigation("InitialCompileJob");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
+ {
+ b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
+ .WithOne("RepositorySettings")
+ .HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Instance");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
+ {
+ b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
+ .WithMany("ActiveTestMerges")
+ .HasForeignKey("RevisionInformationId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge")
+ .WithMany("RevisonInformations")
+ .HasForeignKey("TestMergeId")
+ .OnDelete(DeleteBehavior.ClientNoAction)
+ .IsRequired();
+
+ b.Navigation("RevisionInformation");
+
+ b.Navigation("TestMerge");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
+ {
+ b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
+ .WithMany("RevisionInformations")
+ .HasForeignKey("InstanceId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Instance");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
+ {
+ b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy")
+ .WithMany("TestMerges")
+ .HasForeignKey("MergedById")
+ .OnDelete(DeleteBehavior.Restrict)
+ .IsRequired();
+
+ b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation")
+ .WithOne("PrimaryTestMerge")
+ .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("MergedBy");
+
+ b.Navigation("PrimaryRevisionInformation");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
+ {
+ b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy")
+ .WithMany("CreatedUsers")
+ .HasForeignKey("CreatedById");
+
+ b.HasOne("Tgstation.Server.Host.Models.UserGroup", "Group")
+ .WithMany("Users")
+ .HasForeignKey("GroupId");
+
+ b.Navigation("CreatedBy");
+
+ b.Navigation("Group");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
+ {
+ b.Navigation("Channels");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
+ {
+ b.Navigation("ChatSettings");
+
+ b.Navigation("DreamDaemonSettings");
+
+ b.Navigation("DreamMakerSettings");
+
+ b.Navigation("InstancePermissionSets");
+
+ b.Navigation("Jobs");
+
+ b.Navigation("RepositorySettings");
+
+ b.Navigation("RevisionInformations");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.PermissionSet", b =>
+ {
+ b.Navigation("InstancePermissionSets");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
+ {
+ b.Navigation("ActiveTestMerges");
+
+ b.Navigation("CompileJobs");
+
+ b.Navigation("PrimaryTestMerge");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
+ {
+ b.Navigation("RevisonInformations");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
+ {
+ b.Navigation("CreatedUsers");
+
+ b.Navigation("OAuthConnections");
+
+ b.Navigation("PermissionSet");
+
+ b.Navigation("TestMerges");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.UserGroup", b =>
+ {
+ b.Navigation("PermissionSet")
+ .IsRequired();
+
+ b.Navigation("Users");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/src/Tgstation.Server.Host/Database/Migrations/20250102194845_SLNormalizeSqlite.cs b/src/Tgstation.Server.Host/Database/Migrations/20250102194845_SLNormalizeSqlite.cs
new file mode 100644
index 0000000000..256b9f142c
--- /dev/null
+++ b/src/Tgstation.Server.Host/Database/Migrations/20250102194845_SLNormalizeSqlite.cs
@@ -0,0 +1,18 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+namespace Tgstation.Server.Host.Database.Migrations
+{
+ ///
+ public partial class SLNormalizeSqlite : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ }
+ }
+}
diff --git a/src/Tgstation.Server.Host/Database/Migrations/SqliteDatabaseContextModelSnapshot.cs b/src/Tgstation.Server.Host/Database/Migrations/SqliteDatabaseContextModelSnapshot.cs
index 3de5ce1013..c5949a7c34 100644
--- a/src/Tgstation.Server.Host/Database/Migrations/SqliteDatabaseContextModelSnapshot.cs
+++ b/src/Tgstation.Server.Host/Database/Migrations/SqliteDatabaseContextModelSnapshot.cs
@@ -4,7 +4,7 @@ using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
-namespace Tgstation.Server.Host.Database
+namespace Tgstation.Server.Host.Database.Migrations
{
[DbContext(typeof(SqliteDatabaseContext))]
partial class SqliteDatabaseContextModelSnapshot : ModelSnapshot
@@ -12,16 +12,15 @@ namespace Tgstation.Server.Host.Database
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
- modelBuilder.HasAnnotation("ProductVersion", "8.0.10");
+ modelBuilder.HasAnnotation("ProductVersion", "9.0.0");
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
{
- b.Property("Id")
+ b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
- b.Property("ChannelLimit")
- .IsRequired()
+ b.Property("ChannelLimit")
.HasColumnType("INTEGER");
b.Property("ConnectionString")
@@ -43,8 +42,7 @@ namespace Tgstation.Server.Host.Database
b.Property("Provider")
.HasColumnType("INTEGER");
- b.Property("ReconnectionInterval")
- .IsRequired()
+ b.Property("ReconnectionInterval")
.HasColumnType("INTEGER");
b.HasKey("Id");
@@ -71,20 +69,16 @@ namespace Tgstation.Server.Host.Database
.HasMaxLength(100)
.HasColumnType("TEXT");
- b.Property("IsAdminChannel")
- .IsRequired()
+ b.Property("IsAdminChannel")
.HasColumnType("INTEGER");
- b.Property("IsSystemChannel")
- .IsRequired()
+ b.Property("IsSystemChannel")
.HasColumnType("INTEGER");
- b.Property("IsUpdatesChannel")
- .IsRequired()
+ b.Property("IsUpdatesChannel")
.HasColumnType("INTEGER");
- b.Property("IsWatchdogChannel")
- .IsRequired()
+ b.Property("IsWatchdogChannel")
.HasColumnType("INTEGER");
b.Property("Tag")
@@ -104,7 +98,7 @@ namespace Tgstation.Server.Host.Database
modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
{
- b.Property("Id")
+ b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
@@ -117,8 +111,7 @@ namespace Tgstation.Server.Host.Database
b.Property("DMApiPatchVersion")
.HasColumnType("INTEGER");
- b.Property("DirectoryName")
- .IsRequired()
+ b.Property("DirectoryName")
.HasColumnType("TEXT");
b.Property("DmeName")
@@ -174,58 +167,46 @@ namespace Tgstation.Server.Host.Database
.HasMaxLength(10000)
.HasColumnType("TEXT");
- b.Property("AllowWebClient")
- .IsRequired()
+ b.Property("AllowWebClient")
.HasColumnType("INTEGER");
- b.Property("AutoStart")
- .IsRequired()
+ b.Property("AutoStart")
.HasColumnType("INTEGER");
- b.Property("DumpOnHealthCheckRestart")
- .IsRequired()
+ b.Property("DumpOnHealthCheckRestart")
.HasColumnType("INTEGER");
- b.Property("HealthCheckSeconds")
- .IsRequired()
+ b.Property("HealthCheckSeconds")
.HasColumnType("INTEGER");
b.Property("InstanceId")
.HasColumnType("INTEGER");
- b.Property("LogOutput")
- .IsRequired()
+ b.Property("LogOutput")
.HasColumnType("INTEGER");
- b.Property("MapThreads")
- .IsRequired()
+ b.Property("MapThreads")
.HasColumnType("INTEGER");
- b.Property("Minidumps")
- .IsRequired()
+ b.Property("Minidumps")
.HasColumnType("INTEGER");
- b.Property("OpenDreamTopicPort")
- .IsRequired()
+ b.Property("OpenDreamTopicPort")
.HasColumnType("INTEGER");
- b.Property("Port")
- .IsRequired()
+ b.Property("Port")
.HasColumnType("INTEGER");
b.Property("SecurityLevel")
.HasColumnType("INTEGER");
- b.Property("StartProfiler")
- .IsRequired()
+ b.Property("StartProfiler")
.HasColumnType("INTEGER");
- b.Property("StartupTimeout")
- .IsRequired()
+ b.Property("StartupTimeout")
.HasColumnType("INTEGER");
- b.Property("TopicRequestTimeout")
- .IsRequired()
+ b.Property("TopicRequestTimeout")
.HasColumnType("INTEGER");
b.Property("Visibility")
@@ -245,8 +226,7 @@ namespace Tgstation.Server.Host.Database
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
- b.Property("ApiValidationPort")
- .IsRequired()
+ b.Property("ApiValidationPort")
.HasColumnType("INTEGER");
b.Property("ApiValidationSecurityLevel")
@@ -266,8 +246,7 @@ namespace Tgstation.Server.Host.Database
.HasMaxLength(10000)
.HasColumnType("TEXT");
- b.Property("Timeout")
- .IsRequired()
+ b.Property("Timeout")
.HasColumnType("TEXT");
b.HasKey("Id");
@@ -280,7 +259,7 @@ namespace Tgstation.Server.Host.Database
modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
{
- b.Property("Id")
+ b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
@@ -299,12 +278,10 @@ namespace Tgstation.Server.Host.Database
.HasMaxLength(1000)
.HasColumnType("TEXT");
- b.Property("AutoUpdateInterval")
- .IsRequired()
+ b.Property("AutoUpdateInterval")
.HasColumnType("INTEGER");
- b.Property("ChatBotLimit")
- .IsRequired()
+ b.Property("ChatBotLimit")
.HasColumnType("INTEGER");
b.Property("ConfigurationType")
@@ -315,8 +292,7 @@ namespace Tgstation.Server.Host.Database
.HasMaxLength(100)
.HasColumnType("TEXT");
- b.Property("Online")
- .IsRequired()
+ b.Property("Online")
.HasColumnType("INTEGER");
b.Property("Path")
@@ -379,7 +355,7 @@ namespace Tgstation.Server.Host.Database
modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
{
- b.Property("Id")
+ b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
@@ -389,8 +365,7 @@ namespace Tgstation.Server.Host.Database
b.Property("CancelRightsType")
.HasColumnType("INTEGER");
- b.Property("Cancelled")
- .IsRequired()
+ b.Property("Cancelled")
.HasColumnType("INTEGER");
b.Property("CancelledById")
@@ -412,8 +387,7 @@ namespace Tgstation.Server.Host.Database
b.Property("JobCode")
.HasColumnType("INTEGER");
- b.Property("StartedAt")
- .IsRequired()
+ b.Property("StartedAt")
.HasColumnType("TEXT");
b.Property("StartedById")
@@ -462,7 +436,7 @@ namespace Tgstation.Server.Host.Database
modelBuilder.Entity("Tgstation.Server.Host.Models.PermissionSet", b =>
{
- b.Property("Id")
+ b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
@@ -491,7 +465,7 @@ namespace Tgstation.Server.Host.Database
modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
{
- b.Property("Id")
+ b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
@@ -546,12 +520,10 @@ namespace Tgstation.Server.Host.Database
.HasMaxLength(10000)
.HasColumnType("TEXT");
- b.Property("AutoUpdatesKeepTestMerges")
- .IsRequired()
+ b.Property("AutoUpdatesKeepTestMerges")
.HasColumnType("INTEGER");
- b.Property("AutoUpdatesSynchronize")
- .IsRequired()
+ b.Property("AutoUpdatesSynchronize")
.HasColumnType("INTEGER");
b.Property("CommitterEmail")
@@ -564,27 +536,22 @@ namespace Tgstation.Server.Host.Database
.HasMaxLength(10000)
.HasColumnType("TEXT");
- b.Property("CreateGitHubDeployments")
- .IsRequired()
+ b.Property("CreateGitHubDeployments")
.HasColumnType("INTEGER");
b.Property("InstanceId")
.HasColumnType("INTEGER");
- b.Property("PostTestMergeComment")
- .IsRequired()
+ b.Property("PostTestMergeComment")
.HasColumnType("INTEGER");
- b.Property("PushTestMergeCommits")
- .IsRequired()
+ b.Property("PushTestMergeCommits")
.HasColumnType("INTEGER");
- b.Property("ShowTestMergeCommitters")
- .IsRequired()
+ b.Property("ShowTestMergeCommitters")
.HasColumnType("INTEGER");
- b.Property("UpdateSubmodules")
- .IsRequired()
+ b.Property("UpdateSubmodules")
.HasColumnType("INTEGER");
b.HasKey("Id");
@@ -673,8 +640,7 @@ namespace Tgstation.Server.Host.Database
b.Property("Number")
.HasColumnType("INTEGER");
- b.Property("PrimaryRevisionInformationId")
- .IsRequired()
+ b.Property("PrimaryRevisionInformationId")
.HasColumnType("INTEGER");
b.Property("TargetCommitSha")
@@ -702,7 +668,7 @@ namespace Tgstation.Server.Host.Database
modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
{
- b.Property("Id")
+ b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
@@ -711,15 +677,13 @@ namespace Tgstation.Server.Host.Database
.HasMaxLength(100)
.HasColumnType("TEXT");
- b.Property