diff --git a/build/Version.props b/build/Version.props
index 200291b706..0469ff0b6c 100644
--- a/build/Version.props
+++ b/build/Version.props
@@ -3,7 +3,7 @@
4.1.0
- 5.0.1
+ 5.1.0
5.0.0
5.0.0
0.1.6
diff --git a/src/Tgstation.Server.Api/Models/ErrorCode.cs b/src/Tgstation.Server.Api/Models/ErrorCode.cs
index 808790fa81..b8ced26207 100644
--- a/src/Tgstation.Server.Api/Models/ErrorCode.cs
+++ b/src/Tgstation.Server.Api/Models/ErrorCode.cs
@@ -289,5 +289,17 @@ namespace Tgstation.Server.Api.Models
///
[Description("Missing user ID!")]
UserMissingId,
+
+ ///
+ /// Attempted to add a when at or above the or it was set to something lower than the existing amount of .
+ ///
+ [Description("Performing this operation would violate the instance's configured chatBotLimit!")]
+ ChatBotMax,
+
+ ///
+ /// Attempted to configure a with more s than the configured limit
+ ///
+ [Description("Set amount of chatChannels exceeds the configured channelLimit!")]
+ ChatBotMaxChannels,
}
}
\ No newline at end of file
diff --git a/src/Tgstation.Server.Api/Models/Instance.cs b/src/Tgstation.Server.Api/Models/Instance.cs
index d0c3f1427d..b8d5afb013 100644
--- a/src/Tgstation.Server.Api/Models/Instance.cs
+++ b/src/Tgstation.Server.Api/Models/Instance.cs
@@ -45,6 +45,12 @@ namespace Tgstation.Server.Api.Models
[Required]
public uint? AutoUpdateInterval { get; set; }
+ ///
+ /// The maximum number of s the may contain.
+ ///
+ [Required]
+ public ushort? ChatBotLimit { get; set; }
+
///
/// The representing a change of
///
diff --git a/src/Tgstation.Server.Api/Models/Internal/ChatBot.cs b/src/Tgstation.Server.Api/Models/Internal/ChatBot.cs
index a49f5f2640..44ae5e5c4d 100644
--- a/src/Tgstation.Server.Api/Models/Internal/ChatBot.cs
+++ b/src/Tgstation.Server.Api/Models/Internal/ChatBot.cs
@@ -32,6 +32,12 @@ namespace Tgstation.Server.Api.Models.Internal
[Range(1, UInt32.MaxValue)]
public uint? ReconnectionInterval { get; set; }
+ ///
+ /// The maximum number of s the may contain.
+ ///
+ [Required]
+ public ushort? ChannelLimit { get; set; }
+
///
/// The used for the connection
///
diff --git a/src/Tgstation.Server.Api/Rights/ChatBotRights.cs b/src/Tgstation.Server.Api/Rights/ChatBotRights.cs
index 7dcc43270e..076a412ac7 100644
--- a/src/Tgstation.Server.Api/Rights/ChatBotRights.cs
+++ b/src/Tgstation.Server.Api/Rights/ChatBotRights.cs
@@ -62,5 +62,10 @@ namespace Tgstation.Server.Api.Rights
/// User can change
///
WriteReconnectionInterval = 512,
+
+ ///
+ /// User can change
+ ///
+ WriteChannelLimit = 1024,
}
}
diff --git a/src/Tgstation.Server.Api/Rights/InstanceManagerRights.cs b/src/Tgstation.Server.Api/Rights/InstanceManagerRights.cs
index 1961b81d13..7356ffc48c 100644
--- a/src/Tgstation.Server.Api/Rights/InstanceManagerRights.cs
+++ b/src/Tgstation.Server.Api/Rights/InstanceManagerRights.cs
@@ -56,6 +56,11 @@ namespace Tgstation.Server.Api.Rights
///
/// User can change
///
- SetAutoUpdate = 256
+ SetAutoUpdate = 256,
+
+ ///
+ /// User can change .
+ ///
+ SetChatBotLimit = 512
}
}
diff --git a/src/Tgstation.Server.Host/Controllers/ChatController.cs b/src/Tgstation.Server.Host/Controllers/ChatController.cs
index a81d93242d..181d095328 100644
--- a/src/Tgstation.Server.Host/Controllers/ChatController.cs
+++ b/src/Tgstation.Server.Host/Controllers/ChatController.cs
@@ -77,6 +77,15 @@ namespace Tgstation.Server.Host.Controllers
if (earlyOut != null)
return earlyOut;
+ var countOfExistingBotsInInstance = await DatabaseContext
+ .ChatBots
+ .Where(x => x.InstanceId == Instance.Id)
+ .CountAsync(cancellationToken)
+ .ConfigureAwait(false);
+
+ if (countOfExistingBotsInInstance >= Instance.ChatBotLimit.Value)
+ return BadRequest(new ErrorMessage(ErrorCode.ChatBotMax));
+
model.Enabled = model.Enabled ?? false;
model.ReconnectionInterval = model.ReconnectionInterval ?? 1;
@@ -217,6 +226,9 @@ namespace Tgstation.Server.Host.Controllers
if (current == default)
return StatusCode((int)HttpStatusCode.Gone);
+ if ((model.Channels?.Count ?? current.Channels.Count) > (model.ChannelLimit ?? current.ChannelLimit.Value))
+ return BadRequest(new ErrorMessage(ErrorCode.ChatBotMaxChannels));
+
var userRights = (ChatBotRights)AuthenticationContext.GetRight(RightsType.ChatBots);
bool anySettingsModified = false;
@@ -304,6 +316,13 @@ namespace Tgstation.Server.Host.Controllers
if (!model.ValidateProviderChannelTypes())
return BadRequest(new ErrorMessage(ErrorCode.ChatBotWrongChannelType));
+ var defaultMaxChannels = (ulong)Math.Max(Models.ChatBot.DefaultChannelLimit, model.Channels?.Count ?? 0);
+ if (defaultMaxChannels > UInt16.MaxValue)
+ return BadRequest(new ErrorMessage(ErrorCode.ChatBotMaxChannels));
+
+ if (forCreation)
+ model.ChannelLimit = model.ChannelLimit ?? (ushort)defaultMaxChannels;
+
return null;
}
}
diff --git a/src/Tgstation.Server.Host/Controllers/InstanceController.cs b/src/Tgstation.Server.Host/Controllers/InstanceController.cs
index 41dfc88889..35d5ffc285 100644
--- a/src/Tgstation.Server.Host/Controllers/InstanceController.cs
+++ b/src/Tgstation.Server.Host/Controllers/InstanceController.cs
@@ -244,6 +244,7 @@ namespace Tgstation.Server.Host.Controllers
Online = false,
Path = model.Path,
AutoUpdateInterval = model.AutoUpdateInterval ?? 0,
+ ChatBotLimit = model.ChatBotLimit ?? Models.Instance.DefaultChatBotLimit,
RepositorySettings = new RepositorySettings
{
CommitterEmail = "tgstation-server@users.noreply.github.com",
@@ -425,9 +426,22 @@ namespace Tgstation.Server.Host.Controllers
if (CheckModified(x => x.AutoUpdateInterval, InstanceManagerRights.SetAutoUpdate)
|| CheckModified(x => x.ConfigurationType, InstanceManagerRights.SetConfiguration)
|| CheckModified(x => x.Name, InstanceManagerRights.Rename)
- || CheckModified(x => x.Online, InstanceManagerRights.SetOnline))
+ || CheckModified(x => x.Online, InstanceManagerRights.SetOnline)
+ || CheckModified(x => x.ChatBotLimit, InstanceManagerRights.SetChatBotLimit))
return Forbid();
+ if (model.ChatBotLimit.HasValue)
+ {
+ var countOfExistingChatBots = await DatabaseContext
+ .ChatBots
+ .Where(x => x.InstanceId == originalModel.Id)
+ .CountAsync(cancellationToken)
+ .ConfigureAwait(false);
+
+ if (countOfExistingChatBots > model.ChatBotLimit.Value)
+ return BadRequest(new ErrorMessage(ErrorCode.ChatBotMax));
+ }
+
// ensure the current user has write privilege on the instance
var usersInstanceUser = await usersInstanceUserTask.ConfigureAwait(false);
if (usersInstanceUser == default)
diff --git a/src/Tgstation.Server.Host/Database/Migrations/20200420175359_MSLimitsOnChat.Designer.cs b/src/Tgstation.Server.Host/Database/Migrations/20200420175359_MSLimitsOnChat.Designer.cs
new file mode 100644
index 0000000000..a998426392
--- /dev/null
+++ b/src/Tgstation.Server.Host/Database/Migrations/20200420175359_MSLimitsOnChat.Designer.cs
@@ -0,0 +1,701 @@
+//
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Tgstation.Server.Host.Database;
+
+namespace Tgstation.Server.Host.Migrations
+{
+ [DbContext(typeof(SqlServerDatabaseContext))]
+ [Migration("20200420175359_MSLimitsOnChat")]
+ partial class MSLimitsOnChat
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "2.2.6-servicing-10079")
+ .HasAnnotation("Relational:MaxIdentifierLength", 128)
+ .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+ b.Property("ChannelLimit");
+
+ b.Property("ConnectionString")
+ .IsRequired()
+ .HasMaxLength(10000);
+
+ b.Property("Enabled");
+
+ b.Property("InstanceId");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(100);
+
+ b.Property("Provider");
+
+ b.Property("ReconnectionInterval");
+
+ b.HasKey("Id");
+
+ b.HasIndex("InstanceId", "Name")
+ .IsUnique();
+
+ b.ToTable("ChatBots");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+ b.Property("ChatSettingsId");
+
+ b.Property("DiscordChannelId")
+ .HasConversion(new ValueConverter(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
+
+ b.Property("IrcChannel")
+ .HasMaxLength(100);
+
+ b.Property("IsAdminChannel")
+ .IsRequired();
+
+ b.Property("IsUpdatesChannel")
+ .IsRequired();
+
+ b.Property("IsWatchdogChannel")
+ .IsRequired();
+
+ b.Property("Tag")
+ .HasMaxLength(10000);
+
+ b.HasKey("Id");
+
+ b.HasIndex("ChatSettingsId", "DiscordChannelId")
+ .IsUnique()
+ .HasFilter("[DiscordChannelId] IS NOT NULL");
+
+ b.HasIndex("ChatSettingsId", "IrcChannel")
+ .IsUnique()
+ .HasFilter("[IrcChannel] IS NOT NULL");
+
+ b.ToTable("ChatChannels");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+ b.Property("ByondVersion")
+ .IsRequired();
+
+ b.Property("DirectoryName")
+ .IsRequired();
+
+ b.Property("DmeName")
+ .IsRequired();
+
+ b.Property("JobId");
+
+ b.Property("MinimumSecurityLevel");
+
+ b.Property("Output")
+ .IsRequired();
+
+ b.Property("RevisionInformationId");
+
+ 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()
+ .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+ b.Property("AccessToken");
+
+ b.Property("AllowWebClient")
+ .IsRequired();
+
+ b.Property("AutoStart")
+ .IsRequired();
+
+ b.Property("InstanceId");
+
+ b.Property("PrimaryPort");
+
+ b.Property("ProcessId");
+
+ b.Property("SecondaryPort");
+
+ b.Property("SecurityLevel");
+
+ b.Property("SoftRestart")
+ .IsRequired();
+
+ b.Property("SoftShutdown")
+ .IsRequired();
+
+ b.Property("StartupTimeout");
+
+ b.HasKey("Id");
+
+ b.HasIndex("InstanceId")
+ .IsUnique();
+
+ b.ToTable("DreamDaemonSettings");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+ b.Property("ApiValidationPort");
+
+ b.Property("ApiValidationSecurityLevel");
+
+ b.Property("InstanceId");
+
+ b.Property("ProjectName")
+ .HasMaxLength(10000);
+
+ b.HasKey("Id");
+
+ b.HasIndex("InstanceId")
+ .IsUnique();
+
+ b.ToTable("DreamMakerSettings");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+ b.Property("AutoUpdateInterval");
+
+ b.Property("ChatBotLimit");
+
+ b.Property("ConfigurationType");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(10000);
+
+ b.Property("Online")
+ .IsRequired();
+
+ b.Property("Path")
+ .IsRequired();
+
+ b.HasKey("Id");
+
+ b.HasIndex("Path")
+ .IsUnique();
+
+ b.ToTable("Instances");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+ b.Property("ByondRights")
+ .HasConversion(new ValueConverter(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
+
+ b.Property("ChatBotRights")
+ .HasConversion(new ValueConverter(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
+
+ b.Property("ConfigurationRights")
+ .HasConversion(new ValueConverter(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
+
+ b.Property("DreamDaemonRights")
+ .HasConversion(new ValueConverter(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
+
+ b.Property("DreamMakerRights")
+ .HasConversion(new ValueConverter(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
+
+ b.Property("InstanceId");
+
+ b.Property("InstanceUserRights")
+ .HasConversion(new ValueConverter(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
+
+ b.Property("RepositoryRights")
+ .HasConversion(new ValueConverter(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
+
+ b.Property("UserId")
+ .IsRequired();
+
+ b.HasKey("Id");
+
+ b.HasIndex("InstanceId");
+
+ b.HasIndex("UserId", "InstanceId")
+ .IsUnique();
+
+ b.ToTable("InstanceUsers");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+ b.Property("CancelRight")
+ .HasConversion(new ValueConverter(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
+
+ b.Property("CancelRightsType")
+ .HasConversion(new ValueConverter(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
+
+ b.Property("Cancelled")
+ .IsRequired();
+
+ b.Property("CancelledById");
+
+ b.Property("Description")
+ .IsRequired();
+
+ b.Property("ExceptionDetails");
+
+ b.Property("InstanceId");
+
+ b.Property("StartedAt")
+ .IsRequired();
+
+ b.Property("StartedById")
+ .IsRequired();
+
+ b.Property("StoppedAt");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CancelledById");
+
+ b.HasIndex("InstanceId");
+
+ b.HasIndex("StartedById");
+
+ b.ToTable("Jobs");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+ b.Property("AccessIdentifier")
+ .IsRequired();
+
+ b.Property("ChatChannelsJson")
+ .IsRequired();
+
+ b.Property("ChatCommandsJson")
+ .IsRequired();
+
+ b.Property("CompileJobId");
+
+ b.Property("IsPrimary");
+
+ b.Property("Port");
+
+ b.Property("ProcessId");
+
+ b.Property("RebootState");
+
+ b.Property("ServerCommandsJson")
+ .IsRequired();
+
+ b.HasKey("Id");
+
+ b.HasIndex("CompileJobId");
+
+ b.ToTable("ReattachInformations");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+ b.Property("AccessToken")
+ .HasMaxLength(10000);
+
+ b.Property("AccessUser")
+ .HasMaxLength(10000);
+
+ b.Property("AutoUpdatesKeepTestMerges")
+ .IsRequired();
+
+ b.Property("AutoUpdatesSynchronize")
+ .IsRequired();
+
+ b.Property("CommitterEmail")
+ .IsRequired()
+ .HasMaxLength(10000);
+
+ b.Property("CommitterName")
+ .IsRequired()
+ .HasMaxLength(10000);
+
+ b.Property("InstanceId");
+
+ b.Property("PostTestMergeComment")
+ .IsRequired();
+
+ b.Property("PushTestMergeCommits")
+ .IsRequired();
+
+ b.Property("ShowTestMergeCommitters")
+ .IsRequired();
+
+ b.HasKey("Id");
+
+ b.HasIndex("InstanceId")
+ .IsUnique();
+
+ b.ToTable("RepositorySettings");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+ b.Property("RevisionInformationId");
+
+ b.Property("TestMergeId");
+
+ b.HasKey("Id");
+
+ b.HasIndex("RevisionInformationId");
+
+ b.HasIndex("TestMergeId");
+
+ b.ToTable("RevInfoTestMerges");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+ b.Property("CommitSha")
+ .IsRequired()
+ .HasMaxLength(40);
+
+ b.Property("InstanceId");
+
+ b.Property("OriginCommitSha")
+ .IsRequired()
+ .HasMaxLength(40);
+
+ b.HasKey("Id");
+
+ b.HasIndex("InstanceId", "CommitSha")
+ .IsUnique();
+
+ b.ToTable("RevisionInformations");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+ b.Property("Author")
+ .IsRequired();
+
+ b.Property("BodyAtMerge")
+ .IsRequired();
+
+ b.Property("Comment")
+ .HasMaxLength(10000);
+
+ b.Property("MergedAt");
+
+ b.Property("MergedById")
+ .IsRequired();
+
+ b.Property("Number");
+
+ b.Property("PrimaryRevisionInformationId")
+ .IsRequired();
+
+ b.Property("PullRequestRevision")
+ .IsRequired()
+ .HasMaxLength(40);
+
+ b.Property("TitleAtMerge")
+ .IsRequired();
+
+ b.Property("Url")
+ .IsRequired();
+
+ 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()
+ .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+ b.Property("AdministrationRights")
+ .HasConversion(new ValueConverter(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
+
+ b.Property("CanonicalName")
+ .IsRequired();
+
+ b.Property("CreatedAt")
+ .IsRequired();
+
+ b.Property("CreatedById");
+
+ b.Property("Enabled")
+ .IsRequired();
+
+ b.Property("InstanceManagerRights")
+ .HasConversion(new ValueConverter(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
+
+ b.Property("LastPasswordUpdate");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(10000);
+
+ b.Property("PasswordHash");
+
+ b.Property("SystemIdentifier");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CanonicalName")
+ .IsUnique();
+
+ b.HasIndex("CreatedById");
+
+ b.HasIndex("SystemIdentifier")
+ .IsUnique()
+ .HasFilter("[SystemIdentifier] IS NOT NULL");
+
+ b.ToTable("Users");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+ b.Property("AlphaId");
+
+ b.Property("AlphaIsActive");
+
+ b.Property("BravoId");
+
+ b.Property("InstanceId");
+
+ b.HasKey("Id");
+
+ b.HasIndex("AlphaId");
+
+ b.HasIndex("BravoId");
+
+ b.HasIndex("InstanceId")
+ .IsUnique();
+
+ b.ToTable("WatchdogReattachInformations");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
+ {
+ b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
+ .WithMany("ChatSettings")
+ .HasForeignKey("InstanceId")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
+ {
+ b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings")
+ .WithMany("Channels")
+ .HasForeignKey("ChatSettingsId")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
+ 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.Restrict);
+
+ b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
+ .WithMany("CompileJobs")
+ .HasForeignKey("RevisionInformationId")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
+ 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);
+ });
+
+ 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);
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
+ {
+ b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
+ .WithMany("InstanceUsers")
+ .HasForeignKey("InstanceId")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("Tgstation.Server.Host.Models.User")
+ .WithMany("InstanceUsers")
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
+ 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);
+
+ b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy")
+ .WithMany()
+ .HasForeignKey("StartedById")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
+ {
+ b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
+ .WithMany()
+ .HasForeignKey("CompileJobId")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
+ 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);
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
+ {
+ b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
+ .WithMany("ActiveTestMerges")
+ .HasForeignKey("RevisionInformationId")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge")
+ .WithMany("RevisonInformations")
+ .HasForeignKey("TestMergeId")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
+ {
+ b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
+ .WithMany("RevisionInformations")
+ .HasForeignKey("InstanceId")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
+ {
+ b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy")
+ .WithMany("TestMerges")
+ .HasForeignKey("MergedById")
+ .OnDelete(DeleteBehavior.Restrict);
+
+ b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation")
+ .WithOne("PrimaryTestMerge")
+ .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
+ .OnDelete(DeleteBehavior.Restrict);
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
+ {
+ b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy")
+ .WithMany("CreatedUsers")
+ .HasForeignKey("CreatedById");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
+ {
+ b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Alpha")
+ .WithMany()
+ .HasForeignKey("AlphaId");
+
+ b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Bravo")
+ .WithMany()
+ .HasForeignKey("BravoId");
+
+ b.HasOne("Tgstation.Server.Host.Models.Instance")
+ .WithOne("WatchdogReattachInformation")
+ .HasForeignKey("Tgstation.Server.Host.Models.WatchdogReattachInformation", "InstanceId")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/src/Tgstation.Server.Host/Database/Migrations/20200420175359_MSLimitsOnChat.cs b/src/Tgstation.Server.Host/Database/Migrations/20200420175359_MSLimitsOnChat.cs
new file mode 100644
index 0000000000..b4ca6db82d
--- /dev/null
+++ b/src/Tgstation.Server.Host/Database/Migrations/20200420175359_MSLimitsOnChat.cs
@@ -0,0 +1,46 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+using System;
+using Tgstation.Server.Host.Models;
+
+namespace Tgstation.Server.Host.Migrations
+{
+ ///
+ /// Adds chat limits for MSSQL.
+ ///
+ public partial class MSLimitsOnChat : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ if (migrationBuilder == null)
+ throw new ArgumentNullException(nameof(migrationBuilder));
+
+ migrationBuilder.AddColumn(
+ name: "ChatBotLimit",
+ table: "Instances",
+ nullable: false,
+ defaultValue: Instance.DefaultChatBotLimit);
+
+ migrationBuilder.AddColumn(
+ name: "ChannelLimit",
+ table: "ChatBots",
+ nullable: false,
+ defaultValue: ChatBot.DefaultChannelLimit);
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ if (migrationBuilder == null)
+ throw new ArgumentNullException(nameof(migrationBuilder));
+
+ migrationBuilder.DropColumn(
+ name: "ChatBotLimit",
+ table: "Instances");
+
+ migrationBuilder.DropColumn(
+ name: "ChannelLimit",
+ table: "ChatBots");
+ }
+ }
+}
diff --git a/src/Tgstation.Server.Host/Database/Migrations/20200420181015_MYLimitsOnChat.Designer.cs b/src/Tgstation.Server.Host/Database/Migrations/20200420181015_MYLimitsOnChat.Designer.cs
new file mode 100644
index 0000000000..0b4559bf69
--- /dev/null
+++ b/src/Tgstation.Server.Host/Database/Migrations/20200420181015_MYLimitsOnChat.Designer.cs
@@ -0,0 +1,676 @@
+//
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Tgstation.Server.Host.Database;
+
+namespace Tgstation.Server.Host.Migrations
+{
+ [DbContext(typeof(MySqlDatabaseContext))]
+ [Migration("20200420181015_MYLimitsOnChat")]
+ partial class MYLimitsOnChat
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "2.2.6-servicing-10079")
+ .HasAnnotation("Relational:MaxIdentifierLength", 64);
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("ChannelLimit")
+ .IsRequired();
+
+ b.Property("ConnectionString")
+ .IsRequired()
+ .HasMaxLength(10000);
+
+ b.Property("Enabled");
+
+ b.Property("InstanceId");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(100);
+
+ b.Property("Provider");
+
+ b.Property("ReconnectionInterval")
+ .IsRequired();
+
+ b.HasKey("Id");
+
+ b.HasIndex("InstanceId", "Name")
+ .IsUnique();
+
+ b.ToTable("ChatBots");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("ChatSettingsId");
+
+ b.Property("DiscordChannelId");
+
+ b.Property("IrcChannel")
+ .HasMaxLength(100);
+
+ b.Property("IsAdminChannel")
+ .IsRequired();
+
+ b.Property("IsUpdatesChannel")
+ .IsRequired();
+
+ b.Property("IsWatchdogChannel")
+ .IsRequired();
+
+ b.Property("Tag")
+ .HasMaxLength(10000);
+
+ 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();
+
+ b.Property("ByondVersion")
+ .IsRequired();
+
+ b.Property("DirectoryName")
+ .IsRequired();
+
+ b.Property("DmeName")
+ .IsRequired();
+
+ b.Property("JobId");
+
+ b.Property("MinimumSecurityLevel");
+
+ b.Property("Output")
+ .IsRequired();
+
+ b.Property("RevisionInformationId");
+
+ 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();
+
+ b.Property("AccessToken");
+
+ b.Property("AllowWebClient")
+ .IsRequired();
+
+ b.Property("AutoStart")
+ .IsRequired();
+
+ b.Property("InstanceId");
+
+ b.Property("PrimaryPort")
+ .IsRequired();
+
+ b.Property("ProcessId");
+
+ b.Property("SecondaryPort")
+ .IsRequired();
+
+ b.Property("SecurityLevel");
+
+ b.Property("SoftRestart")
+ .IsRequired();
+
+ b.Property("SoftShutdown")
+ .IsRequired();
+
+ b.Property("StartupTimeout")
+ .IsRequired();
+
+ b.HasKey("Id");
+
+ b.HasIndex("InstanceId")
+ .IsUnique();
+
+ b.ToTable("DreamDaemonSettings");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("ApiValidationPort")
+ .IsRequired();
+
+ b.Property("ApiValidationSecurityLevel");
+
+ b.Property("InstanceId");
+
+ b.Property("ProjectName")
+ .HasMaxLength(10000);
+
+ b.HasKey("Id");
+
+ b.HasIndex("InstanceId")
+ .IsUnique();
+
+ b.ToTable("DreamMakerSettings");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("AutoUpdateInterval")
+ .IsRequired();
+
+ b.Property("ChatBotLimit")
+ .IsRequired();
+
+ b.Property("ConfigurationType");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(10000);
+
+ b.Property("Online")
+ .IsRequired();
+
+ b.Property("Path")
+ .IsRequired();
+
+ b.HasKey("Id");
+
+ b.HasIndex("Path")
+ .IsUnique();
+
+ b.ToTable("Instances");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("ByondRights");
+
+ b.Property("ChatBotRights");
+
+ b.Property("ConfigurationRights");
+
+ b.Property("DreamDaemonRights");
+
+ b.Property("DreamMakerRights");
+
+ b.Property("InstanceId");
+
+ b.Property("InstanceUserRights");
+
+ b.Property("RepositoryRights");
+
+ b.Property("UserId")
+ .IsRequired();
+
+ b.HasKey("Id");
+
+ b.HasIndex("InstanceId");
+
+ b.HasIndex("UserId", "InstanceId")
+ .IsUnique();
+
+ b.ToTable("InstanceUsers");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("CancelRight");
+
+ b.Property("CancelRightsType");
+
+ b.Property("Cancelled")
+ .IsRequired();
+
+ b.Property("CancelledById");
+
+ b.Property("Description")
+ .IsRequired();
+
+ b.Property("ExceptionDetails");
+
+ b.Property("InstanceId");
+
+ b.Property("StartedAt")
+ .IsRequired();
+
+ b.Property("StartedById")
+ .IsRequired();
+
+ b.Property("StoppedAt");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CancelledById");
+
+ b.HasIndex("InstanceId");
+
+ b.HasIndex("StartedById");
+
+ b.ToTable("Jobs");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("AccessIdentifier")
+ .IsRequired();
+
+ b.Property("ChatChannelsJson")
+ .IsRequired();
+
+ b.Property("ChatCommandsJson")
+ .IsRequired();
+
+ b.Property("CompileJobId");
+
+ b.Property("IsPrimary");
+
+ b.Property("Port");
+
+ b.Property("ProcessId");
+
+ b.Property("RebootState");
+
+ b.Property("ServerCommandsJson")
+ .IsRequired();
+
+ b.HasKey("Id");
+
+ b.HasIndex("CompileJobId");
+
+ b.ToTable("ReattachInformations");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("AccessToken")
+ .HasMaxLength(10000);
+
+ b.Property("AccessUser")
+ .HasMaxLength(10000);
+
+ b.Property("AutoUpdatesKeepTestMerges")
+ .IsRequired();
+
+ b.Property("AutoUpdatesSynchronize")
+ .IsRequired();
+
+ b.Property("CommitterEmail")
+ .IsRequired()
+ .HasMaxLength(10000);
+
+ b.Property("CommitterName")
+ .IsRequired()
+ .HasMaxLength(10000);
+
+ b.Property("InstanceId");
+
+ b.Property("PostTestMergeComment")
+ .IsRequired();
+
+ b.Property("PushTestMergeCommits")
+ .IsRequired();
+
+ b.Property("ShowTestMergeCommitters")
+ .IsRequired();
+
+ b.HasKey("Id");
+
+ b.HasIndex("InstanceId")
+ .IsUnique();
+
+ b.ToTable("RepositorySettings");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("RevisionInformationId");
+
+ b.Property("TestMergeId");
+
+ b.HasKey("Id");
+
+ b.HasIndex("RevisionInformationId");
+
+ b.HasIndex("TestMergeId");
+
+ b.ToTable("RevInfoTestMerges");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("CommitSha")
+ .IsRequired()
+ .HasMaxLength(40);
+
+ b.Property("InstanceId");
+
+ b.Property("OriginCommitSha")
+ .IsRequired()
+ .HasMaxLength(40);
+
+ b.HasKey("Id");
+
+ b.HasIndex("InstanceId", "CommitSha")
+ .IsUnique();
+
+ b.ToTable("RevisionInformations");
+ });
+
+ modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Author")
+ .IsRequired();
+
+ b.Property("BodyAtMerge")
+ .IsRequired();
+
+ b.Property("Comment")
+ .HasMaxLength(10000);
+
+ b.Property("MergedAt");
+
+ b.Property("MergedById")
+ .IsRequired();
+
+ b.Property("Number");
+
+ b.Property("PrimaryRevisionInformationId")
+ .IsRequired();
+
+ b.Property("PullRequestRevision")
+ .IsRequired()
+ .HasMaxLength(40);
+
+ b.Property("TitleAtMerge")
+ .IsRequired();
+
+ b.Property("Url")
+ .IsRequired();
+
+ 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();
+
+ b.Property("AdministrationRights");
+
+ b.Property("CanonicalName")
+ .IsRequired();
+
+ b.Property("CreatedAt")
+ .IsRequired();
+
+ b.Property("CreatedById");
+
+ b.Property("Enabled")
+ .IsRequired();
+
+ b.Property("InstanceManagerRights");
+
+ b.Property("LastPasswordUpdate");
+
+ b.Property