From cc2cdeb941b2b07f87839c03909f582d4726ee5f Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Mon, 20 Apr 2020 14:25:52 -0400 Subject: [PATCH 1/3] Add chat bot and channel limits - Add 2 new error codes and permissions - API to 5.1.0 - Adds migration --- build/Version.props | 2 +- src/Tgstation.Server.Api/Models/ErrorCode.cs | 12 + src/Tgstation.Server.Api/Models/Instance.cs | 6 + .../Models/Internal/ChatBot.cs | 6 + .../Rights/ChatBotRights.cs | 5 + .../Rights/InstanceManagerRights.cs | 7 +- .../Controllers/ChatController.cs | 19 + .../Controllers/InstanceController.cs | 16 +- .../20200420175359_MSLimitsOnChat.Designer.cs | 701 ++++++++++++++++++ .../20200420175359_MSLimitsOnChat.cs | 46 ++ .../20200420181015_MYLimitsOnChat.Designer.cs | 676 +++++++++++++++++ .../20200420181015_MYLimitsOnChat.cs | 46 ++ .../20200420181612_SLLimitsOnChat.Designer.cs | 675 +++++++++++++++++ .../20200420181612_SLLimitsOnChat.cs | 46 ++ .../MySqlDatabaseContextModelSnapshot.cs | 21 +- .../SqlServerDatabaseContextModelSnapshot.cs | 19 +- .../SqliteDatabaseContextModelSnapshot.cs | 21 +- src/Tgstation.Server.Host/Models/ChatBot.cs | 5 + .../Models/ChatChannel.cs | 2 +- src/Tgstation.Server.Host/Models/Instance.cs | 5 + 20 files changed, 2305 insertions(+), 31 deletions(-) create mode 100644 src/Tgstation.Server.Host/Database/Migrations/20200420175359_MSLimitsOnChat.Designer.cs create mode 100644 src/Tgstation.Server.Host/Database/Migrations/20200420175359_MSLimitsOnChat.cs create mode 100644 src/Tgstation.Server.Host/Database/Migrations/20200420181015_MYLimitsOnChat.Designer.cs create mode 100644 src/Tgstation.Server.Host/Database/Migrations/20200420181015_MYLimitsOnChat.cs create mode 100644 src/Tgstation.Server.Host/Database/Migrations/20200420181612_SLLimitsOnChat.Designer.cs create mode 100644 src/Tgstation.Server.Host/Database/Migrations/20200420181612_SLLimitsOnChat.cs 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("Name") + .IsRequired() + .HasMaxLength(10000); + + b.Property("PasswordHash"); + + b.Property("SystemIdentifier"); + + b.HasKey("Id"); + + b.HasIndex("CanonicalName") + .IsUnique(); + + b.HasIndex("CreatedById"); + + b.HasIndex("SystemIdentifier") + .IsUnique(); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + 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/20200420181015_MYLimitsOnChat.cs b/src/Tgstation.Server.Host/Database/Migrations/20200420181015_MYLimitsOnChat.cs new file mode 100644 index 0000000000..6238fc5291 --- /dev/null +++ b/src/Tgstation.Server.Host/Database/Migrations/20200420181015_MYLimitsOnChat.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 MYSQL. + /// + public partial class MYLimitsOnChat : 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/20200420181612_SLLimitsOnChat.Designer.cs b/src/Tgstation.Server.Host/Database/Migrations/20200420181612_SLLimitsOnChat.Designer.cs new file mode 100644 index 0000000000..c57cd5635c --- /dev/null +++ b/src/Tgstation.Server.Host/Database/Migrations/20200420181612_SLLimitsOnChat.Designer.cs @@ -0,0 +1,675 @@ +// +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(SqliteDatabaseContext))] + [Migration("20200420181612_SLLimitsOnChat")] + partial class SLLimitsOnChat + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.6-servicing-10079"); + + 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("Name") + .IsRequired() + .HasMaxLength(10000); + + b.Property("PasswordHash"); + + b.Property("SystemIdentifier"); + + b.HasKey("Id"); + + b.HasIndex("CanonicalName") + .IsUnique(); + + b.HasIndex("CreatedById"); + + b.HasIndex("SystemIdentifier") + .IsUnique(); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + 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/20200420181612_SLLimitsOnChat.cs b/src/Tgstation.Server.Host/Database/Migrations/20200420181612_SLLimitsOnChat.cs new file mode 100644 index 0000000000..ca1cdb88c6 --- /dev/null +++ b/src/Tgstation.Server.Host/Database/Migrations/20200420181612_SLLimitsOnChat.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 SQLite. + /// + public partial class SLLimitsOnChat : 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/MySqlDatabaseContextModelSnapshot.cs b/src/Tgstation.Server.Host/Database/Migrations/MySqlDatabaseContextModelSnapshot.cs index 495a245164..448e03d32c 100644 --- a/src/Tgstation.Server.Host/Database/Migrations/MySqlDatabaseContextModelSnapshot.cs +++ b/src/Tgstation.Server.Host/Database/Migrations/MySqlDatabaseContextModelSnapshot.cs @@ -5,13 +5,9 @@ using Microsoft.EntityFrameworkCore.Infrastructure; namespace Tgstation.Server.Host.Database.Migrations { - /// - /// Model snapshot for MYSQL. - /// [DbContext(typeof(MySqlDatabaseContext))] partial class MySqlDatabaseContextModelSnapshot : ModelSnapshot { - /// protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 @@ -24,6 +20,9 @@ namespace Tgstation.Server.Host.Database.Migrations b.Property("Id") .ValueGeneratedOnAdd(); + b.Property("ChannelLimit") + .IsRequired(); + b.Property("ConnectionString") .IsRequired() .HasMaxLength(10000); @@ -192,6 +191,9 @@ namespace Tgstation.Server.Host.Database.Migrations b.Property("AutoUpdateInterval") .IsRequired(); + b.Property("ChatBotLimit") + .IsRequired(); + b.Property("ConfigurationType"); b.Property("Name") @@ -270,7 +272,8 @@ namespace Tgstation.Server.Host.Database.Migrations b.Property("StartedAt") .IsRequired(); - b.Property("StartedById"); + b.Property("StartedById") + .IsRequired(); b.Property("StoppedAt"); @@ -420,11 +423,11 @@ namespace Tgstation.Server.Host.Database.Migrations b.Property("MergedAt"); - b.Property("MergedById"); - - b.Property("Number") + b.Property("MergedById") .IsRequired(); + b.Property("Number"); + b.Property("PrimaryRevisionInformationId") .IsRequired(); @@ -450,7 +453,7 @@ namespace Tgstation.Server.Host.Database.Migrations modelBuilder.Entity("Tgstation.Server.Host.Models.User", b => { - b.Property("Id") + b.Property("Id") .ValueGeneratedOnAdd(); b.Property("AdministrationRights"); diff --git a/src/Tgstation.Server.Host/Database/Migrations/SqlServerDatabaseContextModelSnapshot.cs b/src/Tgstation.Server.Host/Database/Migrations/SqlServerDatabaseContextModelSnapshot.cs index 1c333f9663..89f221a99d 100644 --- a/src/Tgstation.Server.Host/Database/Migrations/SqlServerDatabaseContextModelSnapshot.cs +++ b/src/Tgstation.Server.Host/Database/Migrations/SqlServerDatabaseContextModelSnapshot.cs @@ -7,13 +7,9 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Tgstation.Server.Host.Database.Migrations { - /// - /// Model snapshot for MYSQL. - /// [DbContext(typeof(SqlServerDatabaseContext))] partial class SqlServerDatabaseContextModelSnapshot : ModelSnapshot { - /// protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 @@ -28,6 +24,8 @@ namespace Tgstation.Server.Host.Database.Migrations .ValueGeneratedOnAdd() .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + b.Property("ChannelLimit"); + b.Property("ConnectionString") .IsRequired() .HasMaxLength(10000); @@ -198,6 +196,8 @@ namespace Tgstation.Server.Host.Database.Migrations b.Property("AutoUpdateInterval"); + b.Property("ChatBotLimit"); + b.Property("ConfigurationType"); b.Property("Name") @@ -287,7 +287,8 @@ namespace Tgstation.Server.Host.Database.Migrations b.Property("StartedAt") .IsRequired(); - b.Property("StartedById"); + b.Property("StartedById") + .IsRequired(); b.Property("StoppedAt"); @@ -442,11 +443,11 @@ namespace Tgstation.Server.Host.Database.Migrations b.Property("MergedAt"); - b.Property("MergedById"); - - b.Property("Number") + b.Property("MergedById") .IsRequired(); + b.Property("Number"); + b.Property("PrimaryRevisionInformationId") .IsRequired(); @@ -472,7 +473,7 @@ namespace Tgstation.Server.Host.Database.Migrations modelBuilder.Entity("Tgstation.Server.Host.Models.User", b => { - b.Property("Id") + b.Property("Id") .ValueGeneratedOnAdd() .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); diff --git a/src/Tgstation.Server.Host/Database/Migrations/SqliteDatabaseContextModelSnapshot.cs b/src/Tgstation.Server.Host/Database/Migrations/SqliteDatabaseContextModelSnapshot.cs index 1b75f11e5d..1e1242c319 100644 --- a/src/Tgstation.Server.Host/Database/Migrations/SqliteDatabaseContextModelSnapshot.cs +++ b/src/Tgstation.Server.Host/Database/Migrations/SqliteDatabaseContextModelSnapshot.cs @@ -5,13 +5,9 @@ using Microsoft.EntityFrameworkCore.Infrastructure; namespace Tgstation.Server.Host.Database.Migrations { - /// - /// Model snapshot for SQLite. - /// [DbContext(typeof(SqliteDatabaseContext))] partial class SqliteDatabaseContextModelSnapshot : ModelSnapshot { - /// protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 @@ -23,6 +19,9 @@ namespace Tgstation.Server.Host.Database.Migrations b.Property("Id") .ValueGeneratedOnAdd(); + b.Property("ChannelLimit") + .IsRequired(); + b.Property("ConnectionString") .IsRequired() .HasMaxLength(10000); @@ -191,6 +190,9 @@ namespace Tgstation.Server.Host.Database.Migrations b.Property("AutoUpdateInterval") .IsRequired(); + b.Property("ChatBotLimit") + .IsRequired(); + b.Property("ConfigurationType"); b.Property("Name") @@ -269,7 +271,8 @@ namespace Tgstation.Server.Host.Database.Migrations b.Property("StartedAt") .IsRequired(); - b.Property("StartedById"); + b.Property("StartedById") + .IsRequired(); b.Property("StoppedAt"); @@ -419,11 +422,11 @@ namespace Tgstation.Server.Host.Database.Migrations b.Property("MergedAt"); - b.Property("MergedById"); - - b.Property("Number") + b.Property("MergedById") .IsRequired(); + b.Property("Number"); + b.Property("PrimaryRevisionInformationId") .IsRequired(); @@ -449,7 +452,7 @@ namespace Tgstation.Server.Host.Database.Migrations modelBuilder.Entity("Tgstation.Server.Host.Models.User", b => { - b.Property("Id") + b.Property("Id") .ValueGeneratedOnAdd(); b.Property("AdministrationRights"); diff --git a/src/Tgstation.Server.Host/Models/ChatBot.cs b/src/Tgstation.Server.Host/Models/ChatBot.cs index 7d7d2c24d6..dda821709e 100644 --- a/src/Tgstation.Server.Host/Models/ChatBot.cs +++ b/src/Tgstation.Server.Host/Models/ChatBot.cs @@ -7,6 +7,11 @@ namespace Tgstation.Server.Host.Models /// public sealed class ChatBot : Api.Models.Internal.ChatBot { + /// + /// Default for . + /// + public const ushort DefaultChannelLimit = 100; + /// /// The /// diff --git a/src/Tgstation.Server.Host/Models/ChatChannel.cs b/src/Tgstation.Server.Host/Models/ChatChannel.cs index 5850f88b9a..d4b7bca702 100644 --- a/src/Tgstation.Server.Host/Models/ChatChannel.cs +++ b/src/Tgstation.Server.Host/Models/ChatChannel.cs @@ -14,7 +14,7 @@ public long ChatSettingsId { get; set; } /// - /// The + /// The . /// public ChatBot ChatSettings { get; set; } diff --git a/src/Tgstation.Server.Host/Models/Instance.cs b/src/Tgstation.Server.Host/Models/Instance.cs index eaf2c50bde..1446ee1138 100644 --- a/src/Tgstation.Server.Host/Models/Instance.cs +++ b/src/Tgstation.Server.Host/Models/Instance.cs @@ -7,6 +7,11 @@ namespace Tgstation.Server.Host.Models /// public sealed class Instance : Api.Models.Instance { + /// + /// Default for . + /// + public const ushort DefaultChatBotLimit = 10; + /// /// The for the /// From 22ff4e67374df0cbe7036fd4855d5246094e6241 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Mon, 20 Apr 2020 15:11:28 -0400 Subject: [PATCH 2/3] Write tests, fix bugs --- .../ApiConflictException.cs | 2 +- .../Controllers/ChatController.cs | 19 ++++-- .../Controllers/InstanceController.cs | 2 +- src/Tgstation.Server.Host/Models/ChatBot.cs | 4 +- src/Tgstation.Server.Host/Models/Instance.cs | 3 +- .../Instance/ChatTest.cs | 67 +++++++++++++++---- .../Instance/InstanceTest.cs | 18 +++-- .../InstanceManagerTest.cs | 5 +- 8 files changed, 90 insertions(+), 30 deletions(-) diff --git a/src/Tgstation.Server.Client/ApiConflictException.cs b/src/Tgstation.Server.Client/ApiConflictException.cs index 29cdaa6aff..e107b9bc25 100644 --- a/src/Tgstation.Server.Client/ApiConflictException.cs +++ b/src/Tgstation.Server.Client/ApiConflictException.cs @@ -5,7 +5,7 @@ using Tgstation.Server.Api.Models; namespace Tgstation.Server.Client { /// - /// Occurs when the server returns an unknown response + /// Occurs when the server returns a bad request response if the is present. The server returned an unknown reponse otherwise. /// public sealed class ApiConflictException : ApiException { diff --git a/src/Tgstation.Server.Host/Controllers/ChatController.cs b/src/Tgstation.Server.Host/Controllers/ChatController.cs index 181d095328..796fb8e94d 100644 --- a/src/Tgstation.Server.Host/Controllers/ChatController.cs +++ b/src/Tgstation.Server.Host/Controllers/ChatController.cs @@ -24,6 +24,7 @@ namespace Tgstation.Server.Host.Controllers /// for managing s /// [Route(Routes.Chat)] + #pragma warning disable CA1506 // TODO: Decomplexify public sealed class ChatController : ApiController { /// @@ -84,7 +85,7 @@ namespace Tgstation.Server.Host.Controllers .ConfigureAwait(false); if (countOfExistingBotsInInstance >= Instance.ChatBotLimit.Value) - return BadRequest(new ErrorMessage(ErrorCode.ChatBotMax)); + return Conflict(new ErrorMessage(ErrorCode.ChatBotMax)); model.Enabled = model.Enabled ?? false; model.ReconnectionInterval = model.ReconnectionInterval ?? 1; @@ -98,7 +99,8 @@ namespace Tgstation.Server.Host.Controllers Channels = model.Channels?.Select(x => ConvertApiChatChannel(x)).ToList() ?? new List(), // important that this isn't null InstanceId = Instance.Id, Provider = model.Provider, - ReconnectionInterval = model.ReconnectionInterval + ReconnectionInterval = model.ReconnectionInterval, + ChannelLimit = model.ChannelLimit }; DatabaseContext.ChatBots.Add(dbModel); @@ -206,8 +208,8 @@ namespace Tgstation.Server.Host.Controllers [TgsAuthorize(ChatBotRights.WriteChannels | ChatBotRights.WriteConnectionString | ChatBotRights.WriteEnabled | ChatBotRights.WriteName | ChatBotRights.WriteProvider)] [ProducesResponseType(200)] [ProducesResponseType(typeof(Api.Models.ChatBot), 200)] - #pragma warning disable CA1502 // TODO: Decomplexify - #pragma warning disable CA1506 +#pragma warning disable CA1502 // TODO: Decomplexify +#pragma warning disable CA1506 public async Task Update([FromBody] Api.Models.ChatBot model, CancellationToken cancellationToken) #pragma warning restore CA1502 #pragma warning restore CA1506 @@ -227,7 +229,13 @@ namespace Tgstation.Server.Host.Controllers return StatusCode((int)HttpStatusCode.Gone); if ((model.Channels?.Count ?? current.Channels.Count) > (model.ChannelLimit ?? current.ChannelLimit.Value)) - return BadRequest(new ErrorMessage(ErrorCode.ChatBotMaxChannels)); + { + // 400 or 409 depends on if the client sent both + var errorMessage = new ErrorMessage(ErrorCode.ChatBotMaxChannels); + if (model.Channels != null && model.ChannelLimit.HasValue) + return BadRequest(errorMessage); + return Conflict(errorMessage); + } var userRights = (ChatBotRights)AuthenticationContext.GetRight(RightsType.ChatBots); @@ -326,4 +334,5 @@ namespace Tgstation.Server.Host.Controllers return null; } } + #pragma warning restore CA1506 } diff --git a/src/Tgstation.Server.Host/Controllers/InstanceController.cs b/src/Tgstation.Server.Host/Controllers/InstanceController.cs index 35d5ffc285..26dcddb004 100644 --- a/src/Tgstation.Server.Host/Controllers/InstanceController.cs +++ b/src/Tgstation.Server.Host/Controllers/InstanceController.cs @@ -439,7 +439,7 @@ namespace Tgstation.Server.Host.Controllers .ConfigureAwait(false); if (countOfExistingChatBots > model.ChatBotLimit.Value) - return BadRequest(new ErrorMessage(ErrorCode.ChatBotMax)); + return Conflict(new ErrorMessage(ErrorCode.ChatBotMax)); } // ensure the current user has write privilege on the instance diff --git a/src/Tgstation.Server.Host/Models/ChatBot.cs b/src/Tgstation.Server.Host/Models/ChatBot.cs index dda821709e..27cdcfaa89 100644 --- a/src/Tgstation.Server.Host/Models/ChatBot.cs +++ b/src/Tgstation.Server.Host/Models/ChatBot.cs @@ -39,7 +39,9 @@ namespace Tgstation.Server.Host.Models Enabled = Enabled, Provider = Provider, Id = Id, - Name = Name + Name = Name, + ChannelLimit = ChannelLimit, + ReconnectionInterval = ReconnectionInterval }; } } diff --git a/src/Tgstation.Server.Host/Models/Instance.cs b/src/Tgstation.Server.Host/Models/Instance.cs index 1446ee1138..19a54970d8 100644 --- a/src/Tgstation.Server.Host/Models/Instance.cs +++ b/src/Tgstation.Server.Host/Models/Instance.cs @@ -63,7 +63,8 @@ namespace Tgstation.Server.Host.Models Id = Id, Name = Name, Path = Path, - Online = Online + Online = Online, + ChatBotLimit = ChatBotLimit }; } } diff --git a/tests/Tgstation.Server.Tests/Instance/ChatTest.cs b/tests/Tgstation.Server.Tests/Instance/ChatTest.cs index be7577110c..825a5f1b6e 100644 --- a/tests/Tgstation.Server.Tests/Instance/ChatTest.cs +++ b/tests/Tgstation.Server.Tests/Instance/ChatTest.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; using Tgstation.Server.Api.Models; +using Tgstation.Server.Client; using Tgstation.Server.Client.Components; namespace Tgstation.Server.Tests.Instance @@ -11,13 +13,17 @@ namespace Tgstation.Server.Tests.Instance sealed class ChatTest { readonly IChatBotsClient chatClient; + readonly IInstanceManagerClient instanceClient; + readonly Api.Models.Instance metadata; - public ChatTest(IChatBotsClient chatBotsClient) + public ChatTest(IChatBotsClient chatClient, IInstanceManagerClient instanceClient, Api.Models.Instance metadata) { - chatClient = chatBotsClient ?? throw new ArgumentNullException(nameof(chatBotsClient)); + this.chatClient = chatClient ?? throw new ArgumentNullException(nameof(chatClient)); + this.instanceClient = instanceClient ?? throw new ArgumentNullException(nameof(instanceClient)); + this.metadata = metadata ?? throw new ArgumentNullException(nameof(metadata)); } - public async Task Run() + public async Task Run(CancellationToken cancellationToken) { var firstBot = new ChatBot { @@ -25,22 +31,23 @@ namespace Tgstation.Server.Tests.Instance Enabled = false, Name = "r4407", Provider = ChatProvider.Discord, - ReconnectionInterval = 1 + ReconnectionInterval = 1, + ChannelLimit = 1 }; - firstBot = await chatClient.Create(firstBot, default); + firstBot = await chatClient.Create(firstBot, cancellationToken); Assert.AreNotEqual(0, firstBot.Id); - var bots = await chatClient.List(default); + var bots = await chatClient.List(cancellationToken); Assert.AreEqual(1, bots.Count); Assert.AreEqual(firstBot.Id, bots.First().Id); - var retrievedBot = await chatClient.GetId(firstBot, default); - Assert.AreEqual(firstBot.Id, retrievedBot); + var retrievedBot = await chatClient.GetId(firstBot, cancellationToken); + Assert.AreEqual(firstBot.Id, retrievedBot.Id); firstBot.Enabled = true; - var updatedBot = await chatClient.Update(firstBot, default); + var updatedBot = await chatClient.Update(firstBot, cancellationToken); Assert.AreEqual(true, updatedBot.Enabled); @@ -56,21 +63,55 @@ namespace Tgstation.Server.Tests.Instance DiscordChannelId = channelId } }; + - updatedBot = await chatClient.Update(firstBot, default); + updatedBot = await chatClient.Update(firstBot, cancellationToken); Assert.AreEqual(true, updatedBot.Enabled); Assert.IsNotNull(updatedBot.Channels); Assert.AreEqual(1, updatedBot.Channels.Count); Assert.AreEqual(true, updatedBot.Channels.First().IsAdminChannel); - Assert.AreEqual(true, updatedBot.Channels.First().IsUpdatesChannel); + Assert.AreEqual(false, updatedBot.Channels.First().IsUpdatesChannel); Assert.AreEqual(true, updatedBot.Channels.First().IsWatchdogChannel); Assert.AreEqual("butt", updatedBot.Channels.First().Tag); Assert.AreEqual(channelId, updatedBot.Channels.First().DiscordChannelId); Assert.IsNull(updatedBot.Channels.First().IrcChannel); - await chatClient.Delete(firstBot, default); - bots = await chatClient.List(default); + await ApiAssert.ThrowsException(() => chatClient.Create(new ChatBot + { + Name = "asdf", + ConnectionString = "asdf", + Provider = ChatProvider.Irc + }, cancellationToken), ErrorCode.ChatBotMax); + + // We limited chat bots and channels to 1, try violating them + updatedBot.Channels.Add( + new ChatChannel + { + IsAdminChannel = true, + IsUpdatesChannel = false, + IsWatchdogChannel = true, + Tag = "butt", + DiscordChannelId = channelId + }); + + await ApiAssert.ThrowsException(() => chatClient.Update(updatedBot, cancellationToken), ErrorCode.ChatBotMaxChannels); + + var oldChannels = updatedBot.Channels; + updatedBot.Channels = null; + updatedBot.ChannelLimit = 0; + await ApiAssert.ThrowsException(() => chatClient.Update(updatedBot, cancellationToken), ErrorCode.ChatBotMaxChannels); + + updatedBot.Channels = oldChannels; + updatedBot.ChannelLimit = null; + await ApiAssert.ThrowsException(() => chatClient.Update(updatedBot, cancellationToken), ErrorCode.ChatBotMaxChannels); + + var instance = metadata.CloneMetadata(); + instance.ChatBotLimit = 0; + await ApiAssert.ThrowsException(() => instanceClient.Update(instance, cancellationToken), ErrorCode.ChatBotMax); + + await chatClient.Delete(firstBot, cancellationToken); + bots = await chatClient.List(cancellationToken); Assert.AreEqual(0, bots.Count); } } diff --git a/tests/Tgstation.Server.Tests/Instance/InstanceTest.cs b/tests/Tgstation.Server.Tests/Instance/InstanceTest.cs index 8ca9076a30..b83c8be9c4 100644 --- a/tests/Tgstation.Server.Tests/Instance/InstanceTest.cs +++ b/tests/Tgstation.Server.Tests/Instance/InstanceTest.cs @@ -1,6 +1,7 @@ using System; using System.Threading; using System.Threading.Tasks; +using Tgstation.Server.Client; using Tgstation.Server.Client.Components; namespace Tgstation.Server.Tests.Instance @@ -8,20 +9,25 @@ namespace Tgstation.Server.Tests.Instance sealed class InstanceTest { readonly IInstanceClient instanceClient; + readonly IInstanceManagerClient instanceManagerClient; - public InstanceTest(IInstanceClient instanceClient) + public InstanceTest(IInstanceClient instanceClient, IInstanceManagerClient instanceManagerClient) { this.instanceClient = instanceClient ?? throw new ArgumentNullException(nameof(instanceClient)); + this.instanceManagerClient = instanceManagerClient ?? throw new ArgumentNullException(nameof(instanceManagerClient)); } public async Task RunTests(CancellationToken cancellationToken) { - var byondTests = new ByondTest(instanceClient.Byond, instanceClient.Jobs); - var configTests = new ConfigurationTest(instanceClient.Configuration, instanceClient.Metadata); + var byondTest = new ByondTest(instanceClient.Byond, instanceClient.Jobs); + var chatTest = new ChatTest(instanceClient.ChatBots, instanceManagerClient, instanceClient.Metadata.CloneMetadata()); + var configTest = new ConfigurationTest(instanceClient.Configuration, instanceClient.Metadata); - var byondTest = byondTests.Run(cancellationToken); - await configTests.Run(cancellationToken).ConfigureAwait(false); - await byondTest.ConfigureAwait(false); + var byondTests = byondTest.Run(cancellationToken); + var chatTests = chatTest.Run(cancellationToken); + await configTest.Run(cancellationToken).ConfigureAwait(false); + await byondTests.ConfigureAwait(false); + await chatTests.ConfigureAwait(false); } } } diff --git a/tests/Tgstation.Server.Tests/InstanceManagerTest.cs b/tests/Tgstation.Server.Tests/InstanceManagerTest.cs index 785769c43c..f94fbcb3d3 100644 --- a/tests/Tgstation.Server.Tests/InstanceManagerTest.cs +++ b/tests/Tgstation.Server.Tests/InstanceManagerTest.cs @@ -30,7 +30,8 @@ namespace Tgstation.Server.Tests { Name = "TestInstance-" + ++counter, Path = Path.Combine(testRootPath, Guid.NewGuid().ToString()), - Online = true + Online = true, + ChatBotLimit = 1 }, cancellationToken); public async Task Run(CancellationToken cancellationToken) @@ -115,7 +116,7 @@ namespace Tgstation.Server.Tests Path = initialPath }, cancellationToken), ErrorCode.InstanceRelocateOnline).ConfigureAwait(false); - var testSuite1 = new InstanceTest(instanceManagerClient.CreateClient(firstTest)); + var testSuite1 = new InstanceTest(instanceManagerClient.CreateClient(firstTest), instanceManagerClient); await testSuite1.RunTests(cancellationToken).ConfigureAwait(false); //can regain permissions on instance without instance user From f59f728725a55e67ca48b069a373d1c71ce57199 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Mon, 20 Apr 2020 16:32:17 -0400 Subject: [PATCH 3/3] Bump client version --- build/Version.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/Version.props b/build/Version.props index 0469ff0b6c..22e5396b6f 100644 --- a/build/Version.props +++ b/build/Version.props @@ -4,7 +4,7 @@ 4.1.0 5.1.0 - 5.0.0 + 5.1.0 5.0.0 0.1.6 1.1.0