diff --git a/build/Version.props b/build/Version.props index adf9a0a533..fb69cd8352 100644 --- a/build/Version.props +++ b/build/Version.props @@ -5,10 +5,10 @@ 6.5.0 5.1.0 - 10.3.0 + 10.4.0 7.0.0 - 13.3.0 - 15.3.0 + 13.4.0 + 15.4.0 7.1.2 5.9.0 1.4.1 diff --git a/src/Tgstation.Server.Api/Models/Instance.cs b/src/Tgstation.Server.Api/Models/Instance.cs index c4ae9dccec..3881eec855 100644 --- a/src/Tgstation.Server.Api/Models/Instance.cs +++ b/src/Tgstation.Server.Api/Models/Instance.cs @@ -31,9 +31,18 @@ public abstract class Instance : NamedEntity /// /// The time interval in minutes the repository is automatically pulled and compiles. 0 disables. /// + /// Auto-updates intervals start counting when set, TGS is started, or from the completion of the previous update. Incompatible with . [Required] public uint? AutoUpdateInterval { get; set; } + /// + /// A cron expression indicating when auto-updates should trigger. Must be a valid 6 part cron schedule (SECONDS MINUTES HOURS DAY/MONTH MONTH DAY/WEEK). Empty disables. + /// + /// Updates will not be triggered if the previous update is still running. Incompatible with . + [Required] + [StringLength(Limits.MaximumStringLength)] + public string? AutoUpdateCron { get; set; } + /// /// The maximum number of chat bots the may contain. /// diff --git a/src/Tgstation.Server.Host/Components/IInstanceCore.cs b/src/Tgstation.Server.Host/Components/IInstanceCore.cs index 78bce9e3f3..c9fb88becc 100644 --- a/src/Tgstation.Server.Host/Components/IInstanceCore.cs +++ b/src/Tgstation.Server.Host/Components/IInstanceCore.cs @@ -45,10 +45,11 @@ public interface IInstanceCore : ILatestCompileJobProvider, IRenameNotifyee IConfiguration Configuration { get; } /// - /// Change the for the . + /// Change the auto-update timing for the . /// - /// The new auto update inteval. + /// The new auto-update inteval. + /// The new auto-update cron schedule. /// A representing the running operation. - ValueTask SetAutoUpdateInterval(uint newInterval); + ValueTask ScheduleAutoUpdate(uint newInterval, string? newCron); } } diff --git a/src/Tgstation.Server.Host/Components/Instance.cs b/src/Tgstation.Server.Host/Components/Instance.cs index a8b9a40721..33376f7a18 100644 --- a/src/Tgstation.Server.Host/Components/Instance.cs +++ b/src/Tgstation.Server.Host/Components/Instance.cs @@ -6,6 +6,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; + +using NCrontab; + using Serilog.Context; using Tgstation.Server.Api.Rights; @@ -183,7 +186,7 @@ public async Task StartAsync(CancellationToken cancellationToken) using (LogContext.PushProperty(SerilogContextHelper.InstanceIdContextProperty, metadata.Id)) { await Task.WhenAll( - SetAutoUpdateInterval(metadata.Require(x => x.AutoUpdateInterval)).AsTask(), + ScheduleAutoUpdate(metadata.Require(x => x.AutoUpdateInterval), metadata.AutoUpdateCron).AsTask(), Configuration.StartAsync(cancellationToken), EngineManager.StartAsync(cancellationToken), Chat.StartAsync(cancellationToken), @@ -202,7 +205,7 @@ public async Task StopAsync(CancellationToken cancellationToken) using (LogContext.PushProperty(SerilogContextHelper.InstanceIdContextProperty, metadata.Id)) { logger.LogDebug("Stopping instance..."); - await SetAutoUpdateInterval(0); + await ScheduleAutoUpdate(0, null); await Watchdog.StopAsync(cancellationToken); await Task.WhenAll( Configuration.StopAsync(cancellationToken), @@ -213,8 +216,11 @@ public async Task StopAsync(CancellationToken cancellationToken) } /// - public async ValueTask SetAutoUpdateInterval(uint newInterval) + public async ValueTask ScheduleAutoUpdate(uint newInterval, string? newCron) { + if (newInterval > 0 && !String.IsNullOrWhiteSpace(newCron)) + throw new ArgumentException("Only one of newInterval and newCron may be set!"); + Task toWait; lock (timerLock) { @@ -232,9 +238,9 @@ public async ValueTask SetAutoUpdateInterval(uint newInterval) } await toWait; - if (newInterval == 0) + if (newInterval == 0 && String.IsNullOrWhiteSpace(newCron)) { - logger.LogTrace("New auto-update interval is 0. Not starting task."); + logger.LogTrace("Auto-update disabled 0. Not starting task."); return; } @@ -243,12 +249,12 @@ public async ValueTask SetAutoUpdateInterval(uint newInterval) // race condition, just quit if (timerTask != null) { - logger.LogWarning("Aborting auto update interval change due to race condition!"); + logger.LogWarning("Aborting auto-update scheduling change due to race condition!"); return; } timerCts = new CancellationTokenSource(); - timerTask = TimerLoop(newInterval, timerCts.Token); + timerTask = TimerLoop(newInterval, newCron, timerCts.Token); } } @@ -484,16 +490,47 @@ async ValueTask UpdateRevInfo(string currentHead, bool onOrigin, IEnumerable. /// /// How many minutes the operation should repeat. Does not include running time. + /// Alternative cron schedule. /// The for the operation. /// A representing the running operation. #pragma warning disable CA1502 // TODO: Decomplexify - async Task TimerLoop(uint minutes, CancellationToken cancellationToken) + async Task TimerLoop(uint minutes, string? cron, CancellationToken cancellationToken) { logger.LogDebug("Entering auto-update loop"); while (true) try { - await asyncDelayer.Delay(TimeSpan.FromMinutes(minutes > Int32.MaxValue ? Int32.MaxValue : minutes), cancellationToken); + TimeSpan delay; + if (cron != null) + { + logger.LogTrace("Using cron schedule: {cron}", cron); + var schedule = CrontabSchedule.Parse( + cron, + new CrontabSchedule.ParseOptions + { + IncludingSeconds = true, + }); + var now = DateTime.UtcNow; + var nextOccurrence = schedule.GetNextOccurrence(now); + delay = nextOccurrence - now; + } + else + { + logger.LogTrace("Using interval: {interval}m", minutes); + if (minutes > Int32.MaxValue) + { + logger.LogWarning( + "Auto-update interval is above the maximum limit of {maxMinutes}m. This is likely a user/client error. Truncating to maximum...", + Int32.MaxValue); + minutes = Int32.MaxValue; + } + + delay = TimeSpan.FromMinutes(minutes); + } + + logger.LogInformation("Next auto-update will occur at {time}", DateTimeOffset.UtcNow + delay); + + await asyncDelayer.Delay(delay, cancellationToken); logger.LogInformation("Beginning auto update..."); await eventConsumer.HandleEvent(EventType.InstanceAutoUpdateStart, Enumerable.Empty(), true, cancellationToken); try diff --git a/src/Tgstation.Server.Host/Components/InstanceWrapper.cs b/src/Tgstation.Server.Host/Components/InstanceWrapper.cs index ba2c84d688..249342fc37 100644 --- a/src/Tgstation.Server.Host/Components/InstanceWrapper.cs +++ b/src/Tgstation.Server.Host/Components/InstanceWrapper.cs @@ -55,7 +55,7 @@ public InstanceWrapper() public ValueTask InstanceRenamed(string newInstanceName, CancellationToken cancellationToken) => Instance.InstanceRenamed(newInstanceName, cancellationToken); /// - public ValueTask SetAutoUpdateInterval(uint newInterval) => Instance.SetAutoUpdateInterval(newInterval); + public ValueTask ScheduleAutoUpdate(uint newInterval, string? newCron) => Instance.ScheduleAutoUpdate(newInterval, newCron); /// public CompileJob? LatestCompileJob() => Instance.LatestCompileJob(); diff --git a/src/Tgstation.Server.Host/Controllers/InstanceController.cs b/src/Tgstation.Server.Host/Controllers/InstanceController.cs index e74d4fdce1..7e3aa809dd 100644 --- a/src/Tgstation.Server.Host/Controllers/InstanceController.cs +++ b/src/Tgstation.Server.Host/Controllers/InstanceController.cs @@ -13,6 +13,8 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using NCrontab; + using Tgstation.Server.Api; using Tgstation.Server.Api.Models; using Tgstation.Server.Api.Models.Request; @@ -144,6 +146,10 @@ public async ValueTask Create([FromBody] InstanceCreateRequest mo if (String.IsNullOrWhiteSpace(model.Name) || String.IsNullOrWhiteSpace(model.Path)) return BadRequest(new ErrorMessageResponse(ErrorCode.InstanceWhitespaceNameOrPath)); + IActionResult? earlyOut = ValidateCronSetting(model); + if (earlyOut != null) + return earlyOut; + var unNormalizedPath = model.Path; var targetInstancePath = NormalizePath(unNormalizedPath); model.Path = targetInstancePath; @@ -166,7 +172,6 @@ bool InstanceIsChildOf(string otherPath) return Conflict(new ErrorMessageResponse(ErrorCode.InstanceAtConflictingPath)); // Validate it's not a child of any other instance - IActionResult? earlyOut = null; ulong countOfOtherInstances = 0; using (var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken)) { @@ -415,9 +420,28 @@ bool CheckModified(Expression> expression, Insta } var oldAutoUpdateInterval = originalModel.AutoUpdateInterval!.Value; + var oldAutoUpdateCron = originalModel.AutoUpdateCron; + + var earlyOut = ValidateCronSetting(model); + if (earlyOut != null) + return earlyOut; + + var changedAutoInterval = model.AutoUpdateInterval.HasValue && oldAutoUpdateInterval != model.AutoUpdateInterval; + var changedAutoCron = model.AutoUpdateCron != null && oldAutoUpdateCron != model.AutoUpdateCron; + + if (changedAutoCron) + { + if (changedAutoInterval) + return BadRequest(new ErrorMessageResponse(ErrorCode.ModelValidationFailure)); + + if (String.IsNullOrWhiteSpace(model.AutoUpdateCron)) + model.AutoUpdateCron = String.Empty; + } + var renamed = model.Name != null && originalModel.Name != model.Name; if (CheckModified(x => x.AutoUpdateInterval, InstanceManagerRights.SetAutoUpdate) + || CheckModified(x => x.AutoUpdateCron, InstanceManagerRights.SetAutoUpdate) || CheckModified(x => x.ConfigurationType, InstanceManagerRights.SetConfiguration) || CheckModified(x => x.Name, InstanceManagerRights.Rename) || CheckModified(x => x.Online, InstanceManagerRights.SetOnline) @@ -497,13 +521,13 @@ bool CheckModified(Expression> expression, Insta api.MoveJob = job.ToApi(); } - if (model.AutoUpdateInterval.HasValue && oldAutoUpdateInterval != model.AutoUpdateInterval) + if (changedAutoInterval || changedAutoCron) { // ignoring retval because we don't care if it's offline await WithComponentInstanceNullable( async componentInstance => { - await componentInstance.SetAutoUpdateInterval(model.AutoUpdateInterval.Value); + await componentInstance.ScheduleAutoUpdate(model.AutoUpdateInterval!.Value, model.AutoUpdateCron); return null; }, originalModel); @@ -746,6 +770,7 @@ public async ValueTask GrantPermissions(long id, CancellationToke Online = false, Path = initialSettings.Path, AutoUpdateInterval = initialSettings.AutoUpdateInterval ?? 0, + AutoUpdateCron = initialSettings.AutoUpdateCron ?? String.Empty, ChatBotLimit = initialSettings.ChatBotLimit ?? Models.Instance.DefaultChatBotLimit, RepositorySettings = new RepositorySettings { @@ -821,5 +846,25 @@ async ValueTask CheckAccessible(InstanceResponse instanceResponse, CancellationT .Where(x => x.InstanceId == instanceResponse.Id && x.PermissionSetId == AuthenticationContext.PermissionSet.Id) .AnyAsync(cancellationToken); } + + /// + /// Validates a given 's setting. + /// + /// The to validate. + /// if has a valid setting, a otherwise. + BadRequestObjectResult? ValidateCronSetting(Api.Models.Instance instance) + { + if (!String.IsNullOrWhiteSpace(instance.AutoUpdateCron) && + ((instance.AutoUpdateInterval.HasValue && instance.AutoUpdateInterval.Value != 0) + || (CrontabSchedule.TryParse( + instance.AutoUpdateCron, + new CrontabSchedule.ParseOptions + { + IncludingSeconds = true, + }) == null))) + return BadRequest(new ErrorMessageResponse(ErrorCode.ModelValidationFailure)); + + return null; + } } } diff --git a/src/Tgstation.Server.Host/Database/DatabaseContext.cs b/src/Tgstation.Server.Host/Database/DatabaseContext.cs index f7293c4f6a..3f95561322 100644 --- a/src/Tgstation.Server.Host/Database/DatabaseContext.cs +++ b/src/Tgstation.Server.Host/Database/DatabaseContext.cs @@ -375,22 +375,22 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) /// /// Used by unit tests to remind us to setup the correct MSSQL migration downgrades. /// - internal static readonly Type MSLatestMigration = typeof(MSSwitchTo64BitDeploymentIds); + internal static readonly Type MSLatestMigration = typeof(MSAddCronAutoUpdates); /// /// Used by unit tests to remind us to setup the correct MYSQL migration downgrades. /// - internal static readonly Type MYLatestMigration = typeof(MYSwitchTo64BitDeploymentIds); + internal static readonly Type MYLatestMigration = typeof(MYAddCronAutoUpdates); /// /// Used by unit tests to remind us to setup the correct PostgresSQL migration downgrades. /// - internal static readonly Type PGLatestMigration = typeof(PGSwitchTo64BitDeploymentIds); + internal static readonly Type PGLatestMigration = typeof(PGAddCronAutoUpdates); /// /// Used by unit tests to remind us to setup the correct SQLite migration downgrades. /// - internal static readonly Type SLLatestMigration = typeof(SLAddCompilerAdditionalArguments); + internal static readonly Type SLLatestMigration = typeof(SLAddCronAutoUpdates); /// #pragma warning disable CA1502 // Cyclomatic complexity diff --git a/src/Tgstation.Server.Host/Database/Migrations/20240701235007_MSAddCronAutoUpdates.Designer.cs b/src/Tgstation.Server.Host/Database/Migrations/20240701235007_MSAddCronAutoUpdates.Designer.cs new file mode 100644 index 0000000000..d06a9ddd1d --- /dev/null +++ b/src/Tgstation.Server.Host/Database/Migrations/20240701235007_MSAddCronAutoUpdates.Designer.cs @@ -0,0 +1,1089 @@ +// +using System; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Tgstation.Server.Host.Database.Migrations +{ + [DbContext(typeof(SqlServerDatabaseContext))] + [Migration("20240701235007_MSAddCronAutoUpdates")] + partial class MSAddCronAutoUpdates + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.6") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ChannelLimit") + .HasColumnType("int"); + + b.Property("ConnectionString") + .IsRequired() + .HasMaxLength(10000) + .HasColumnType("nvarchar(max)"); + + b.Property("Enabled") + .HasColumnType("bit"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Provider") + .HasColumnType("int"); + + b.Property("ReconnectionInterval") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId", "Name") + .IsUnique(); + + b.ToTable("ChatBots"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ChatSettingsId") + .HasColumnType("bigint"); + + b.Property("DiscordChannelId") + .HasColumnType("decimal(20,0)"); + + b.Property("IrcChannel") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("IsAdminChannel") + .IsRequired() + .HasColumnType("bit"); + + b.Property("IsSystemChannel") + .IsRequired() + .HasColumnType("bit"); + + b.Property("IsUpdatesChannel") + .IsRequired() + .HasColumnType("bit"); + + b.Property("IsWatchdogChannel") + .IsRequired() + .HasColumnType("bit"); + + b.Property("Tag") + .HasMaxLength(10000) + .HasColumnType("nvarchar(max)"); + + 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() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DMApiMajorVersion") + .HasColumnType("int"); + + b.Property("DMApiMinorVersion") + .HasColumnType("int"); + + b.Property("DMApiPatchVersion") + .HasColumnType("int"); + + b.Property("DirectoryName") + .IsRequired() + .HasColumnType("uniqueidentifier"); + + b.Property("DmeName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("EngineVersion") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("GitHubDeploymentId") + .HasColumnType("bigint"); + + b.Property("GitHubRepoId") + .HasColumnType("bigint"); + + b.Property("JobId") + .HasColumnType("bigint"); + + b.Property("MinimumSecurityLevel") + .HasColumnType("int"); + + b.Property("Output") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("RepositoryOrigin") + .HasColumnType("nvarchar(max)"); + + b.Property("RevisionInformationId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("DirectoryName"); + + b.HasIndex("JobId") + .IsUnique(); + + b.HasIndex("RevisionInformationId"); + + b.ToTable("CompileJobs"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AdditionalParameters") + .IsRequired() + .HasMaxLength(10000) + .HasColumnType("nvarchar(max)"); + + b.Property("AllowWebClient") + .IsRequired() + .HasColumnType("bit"); + + b.Property("AutoStart") + .IsRequired() + .HasColumnType("bit"); + + b.Property("DumpOnHealthCheckRestart") + .IsRequired() + .HasColumnType("bit"); + + b.Property("HealthCheckSeconds") + .HasColumnType("bigint"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("LogOutput") + .IsRequired() + .HasColumnType("bit"); + + b.Property("MapThreads") + .HasColumnType("bigint"); + + b.Property("Minidumps") + .IsRequired() + .HasColumnType("bit"); + + b.Property("Port") + .HasColumnType("int"); + + b.Property("SecurityLevel") + .HasColumnType("int"); + + b.Property("StartProfiler") + .IsRequired() + .HasColumnType("bit"); + + b.Property("StartupTimeout") + .HasColumnType("bigint"); + + b.Property("TopicRequestTimeout") + .HasColumnType("bigint"); + + b.Property("Visibility") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("DreamDaemonSettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ApiValidationPort") + .HasColumnType("int"); + + b.Property("ApiValidationSecurityLevel") + .HasColumnType("int"); + + b.Property("CompilerAdditionalArguments") + .HasMaxLength(10000) + .HasColumnType("nvarchar(max)"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("ProjectName") + .HasMaxLength(10000) + .HasColumnType("nvarchar(max)"); + + b.Property("RequireDMApiValidation") + .IsRequired() + .HasColumnType("bit"); + + b.Property("Timeout") + .IsRequired() + .HasColumnType("time"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("DreamMakerSettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AutoUpdateCron") + .IsRequired() + .HasMaxLength(10000) + .HasColumnType("nvarchar(max)"); + + b.Property("AutoUpdateInterval") + .HasColumnType("bigint"); + + b.Property("ChatBotLimit") + .HasColumnType("int"); + + b.Property("ConfigurationType") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Online") + .IsRequired() + .HasColumnType("bit"); + + b.Property("Path") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("SwarmIdentifer") + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("Path", "SwarmIdentifer") + .IsUnique() + .HasFilter("[SwarmIdentifer] IS NOT NULL"); + + b.ToTable("Instances"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.InstancePermissionSet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ChatBotRights") + .HasColumnType("decimal(20,0)"); + + b.Property("ConfigurationRights") + .HasColumnType("decimal(20,0)"); + + b.Property("DreamDaemonRights") + .HasColumnType("decimal(20,0)"); + + b.Property("DreamMakerRights") + .HasColumnType("decimal(20,0)"); + + b.Property("EngineRights") + .HasColumnType("decimal(20,0)"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("InstancePermissionSetRights") + .HasColumnType("decimal(20,0)"); + + b.Property("PermissionSetId") + .HasColumnType("bigint"); + + b.Property("RepositoryRights") + .HasColumnType("decimal(20,0)"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId"); + + b.HasIndex("PermissionSetId", "InstanceId") + .IsUnique(); + + b.ToTable("InstancePermissionSets"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CancelRight") + .HasColumnType("decimal(20,0)"); + + b.Property("CancelRightsType") + .HasColumnType("decimal(20,0)"); + + b.Property("Cancelled") + .IsRequired() + .HasColumnType("bit"); + + b.Property("CancelledById") + .HasColumnType("bigint"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ErrorCode") + .HasColumnType("bigint"); + + b.Property("ExceptionDetails") + .HasColumnType("nvarchar(max)"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("JobCode") + .HasColumnType("tinyint"); + + b.Property("StartedAt") + .IsRequired() + .HasColumnType("datetimeoffset"); + + b.Property("StartedById") + .HasColumnType("bigint"); + + b.Property("StoppedAt") + .HasColumnType("datetimeoffset"); + + b.HasKey("Id"); + + b.HasIndex("CancelledById"); + + b.HasIndex("InstanceId"); + + b.HasIndex("StartedById"); + + b.ToTable("Jobs"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ExternalUserId") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Provider") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("Provider", "ExternalUserId") + .IsUnique(); + + b.ToTable("OAuthConnections"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.PermissionSet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AdministrationRights") + .HasColumnType("decimal(20,0)"); + + b.Property("GroupId") + .HasColumnType("bigint"); + + b.Property("InstanceManagerRights") + .HasColumnType("decimal(20,0)"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("GroupId") + .IsUnique() + .HasFilter("[GroupId] IS NOT NULL"); + + b.HasIndex("UserId") + .IsUnique() + .HasFilter("[UserId] IS NOT NULL"); + + b.ToTable("PermissionSets"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AccessIdentifier") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CompileJobId") + .HasColumnType("bigint"); + + b.Property("InitialCompileJobId") + .HasColumnType("bigint"); + + b.Property("LaunchSecurityLevel") + .HasColumnType("int"); + + b.Property("LaunchVisibility") + .HasColumnType("int"); + + b.Property("Port") + .HasColumnType("int"); + + b.Property("ProcessId") + .HasColumnType("int"); + + b.Property("RebootState") + .HasColumnType("int"); + + b.Property("TopicPort") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("CompileJobId"); + + b.HasIndex("InitialCompileJobId"); + + b.ToTable("ReattachInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AccessToken") + .HasMaxLength(10000) + .HasColumnType("nvarchar(max)"); + + b.Property("AccessUser") + .HasMaxLength(10000) + .HasColumnType("nvarchar(max)"); + + b.Property("AutoUpdatesKeepTestMerges") + .IsRequired() + .HasColumnType("bit"); + + b.Property("AutoUpdatesSynchronize") + .IsRequired() + .HasColumnType("bit"); + + b.Property("CommitterEmail") + .IsRequired() + .HasMaxLength(10000) + .HasColumnType("nvarchar(max)"); + + b.Property("CommitterName") + .IsRequired() + .HasMaxLength(10000) + .HasColumnType("nvarchar(max)"); + + b.Property("CreateGitHubDeployments") + .IsRequired() + .HasColumnType("bit"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("PostTestMergeComment") + .IsRequired() + .HasColumnType("bit"); + + b.Property("PushTestMergeCommits") + .IsRequired() + .HasColumnType("bit"); + + b.Property("ShowTestMergeCommitters") + .IsRequired() + .HasColumnType("bit"); + + b.Property("UpdateSubmodules") + .IsRequired() + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("RepositorySettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("RevisionInformationId") + .HasColumnType("bigint"); + + b.Property("TestMergeId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("RevisionInformationId"); + + b.HasIndex("TestMergeId"); + + b.ToTable("RevInfoTestMerges"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CommitSha") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("OriginCommitSha") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)"); + + b.Property("Timestamp") + .HasColumnType("datetimeoffset"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId", "CommitSha") + .IsUnique(); + + b.ToTable("RevisionInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Author") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("BodyAtMerge") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Comment") + .HasMaxLength(10000) + .HasColumnType("nvarchar(max)"); + + b.Property("MergedAt") + .HasColumnType("datetimeoffset"); + + b.Property("MergedById") + .HasColumnType("bigint"); + + b.Property("Number") + .HasColumnType("int"); + + b.Property("PrimaryRevisionInformationId") + .IsRequired() + .HasColumnType("bigint"); + + b.Property("TargetCommitSha") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)"); + + b.Property("TitleAtMerge") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Url") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("MergedById"); + + b.HasIndex("PrimaryRevisionInformationId") + .IsUnique(); + + b.ToTable("TestMerges"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CanonicalName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("CreatedAt") + .IsRequired() + .HasColumnType("datetimeoffset"); + + b.Property("CreatedById") + .HasColumnType("bigint"); + + b.Property("Enabled") + .IsRequired() + .HasColumnType("bit"); + + b.Property("GroupId") + .HasColumnType("bigint"); + + b.Property("LastPasswordUpdate") + .HasColumnType("datetimeoffset"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("SystemIdentifier") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("Id"); + + b.HasIndex("CanonicalName") + .IsUnique(); + + b.HasIndex("CreatedById"); + + b.HasIndex("GroupId"); + + b.HasIndex("SystemIdentifier") + .IsUnique() + .HasFilter("[SystemIdentifier] IS NOT NULL"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.UserGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("Groups"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("ChatSettings") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Instance"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b => + { + b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings") + .WithMany("Channels") + .HasForeignKey("ChatSettingsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChatSettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b => + { + b.HasOne("Tgstation.Server.Host.Models.Job", "Job") + .WithOne() + .HasForeignKey("Tgstation.Server.Host.Models.CompileJob", "JobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation") + .WithMany("CompileJobs") + .HasForeignKey("RevisionInformationId") + .OnDelete(DeleteBehavior.ClientNoAction) + .IsRequired(); + + b.Navigation("Job"); + + b.Navigation("RevisionInformation"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithOne("DreamDaemonSettings") + .HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Instance"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithOne("DreamMakerSettings") + .HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Instance"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.InstancePermissionSet", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("InstancePermissionSets") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.PermissionSet", "PermissionSet") + .WithMany("InstancePermissionSets") + .HasForeignKey("PermissionSetId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Instance"); + + b.Navigation("PermissionSet"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy") + .WithMany() + .HasForeignKey("CancelledById"); + + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("Jobs") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy") + .WithMany() + .HasForeignKey("StartedById") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CancelledBy"); + + b.Navigation("Instance"); + + b.Navigation("StartedBy"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "User") + .WithMany("OAuthConnections") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.PermissionSet", b => + { + b.HasOne("Tgstation.Server.Host.Models.UserGroup", "Group") + .WithOne("PermissionSet") + .HasForeignKey("Tgstation.Server.Host.Models.PermissionSet", "GroupId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Tgstation.Server.Host.Models.User", "User") + .WithOne("PermissionSet") + .HasForeignKey("Tgstation.Server.Host.Models.PermissionSet", "UserId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Group"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b => + { + b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob") + .WithMany() + .HasForeignKey("CompileJobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.CompileJob", "InitialCompileJob") + .WithMany() + .HasForeignKey("InitialCompileJobId"); + + b.Navigation("CompileJob"); + + b.Navigation("InitialCompileJob"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithOne("RepositorySettings") + .HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Instance"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b => + { + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation") + .WithMany("ActiveTestMerges") + .HasForeignKey("RevisionInformationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge") + .WithMany("RevisonInformations") + .HasForeignKey("TestMergeId") + .OnDelete(DeleteBehavior.ClientNoAction) + .IsRequired(); + + b.Navigation("RevisionInformation"); + + b.Navigation("TestMerge"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("RevisionInformations") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Instance"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy") + .WithMany("TestMerges") + .HasForeignKey("MergedById") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation") + .WithOne("PrimaryTestMerge") + .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MergedBy"); + + b.Navigation("PrimaryRevisionInformation"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.User", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy") + .WithMany("CreatedUsers") + .HasForeignKey("CreatedById"); + + b.HasOne("Tgstation.Server.Host.Models.UserGroup", "Group") + .WithMany("Users") + .HasForeignKey("GroupId"); + + b.Navigation("CreatedBy"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => + { + b.Navigation("Channels"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b => + { + b.Navigation("ChatSettings"); + + b.Navigation("DreamDaemonSettings"); + + b.Navigation("DreamMakerSettings"); + + b.Navigation("InstancePermissionSets"); + + b.Navigation("Jobs"); + + b.Navigation("RepositorySettings"); + + b.Navigation("RevisionInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.PermissionSet", b => + { + b.Navigation("InstancePermissionSets"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b => + { + b.Navigation("ActiveTestMerges"); + + b.Navigation("CompileJobs"); + + b.Navigation("PrimaryTestMerge"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b => + { + b.Navigation("RevisonInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.User", b => + { + b.Navigation("CreatedUsers"); + + b.Navigation("OAuthConnections"); + + b.Navigation("PermissionSet"); + + b.Navigation("TestMerges"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.UserGroup", b => + { + b.Navigation("PermissionSet") + .IsRequired(); + + b.Navigation("Users"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Tgstation.Server.Host/Database/Migrations/20240701235007_MSAddCronAutoUpdates.cs b/src/Tgstation.Server.Host/Database/Migrations/20240701235007_MSAddCronAutoUpdates.cs new file mode 100644 index 0000000000..b79f0f58cf --- /dev/null +++ b/src/Tgstation.Server.Host/Database/Migrations/20240701235007_MSAddCronAutoUpdates.cs @@ -0,0 +1,34 @@ +using System; + +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Tgstation.Server.Host.Database.Migrations +{ + /// + public partial class MSAddCronAutoUpdates : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + ArgumentNullException.ThrowIfNull(migrationBuilder); + + migrationBuilder.AddColumn( + name: "AutoUpdateCron", + table: "Instances", + type: "nvarchar(max)", + maxLength: 10000, + nullable: false, + defaultValue: String.Empty); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + ArgumentNullException.ThrowIfNull(migrationBuilder); + + migrationBuilder.DropColumn( + name: "AutoUpdateCron", + table: "Instances"); + } + } +} diff --git a/src/Tgstation.Server.Host/Database/Migrations/20240701235015_MYAddCronAutoUpdates.Designer.cs b/src/Tgstation.Server.Host/Database/Migrations/20240701235015_MYAddCronAutoUpdates.Designer.cs new file mode 100644 index 0000000000..64f0251d4f --- /dev/null +++ b/src/Tgstation.Server.Host/Database/Migrations/20240701235015_MYAddCronAutoUpdates.Designer.cs @@ -0,0 +1,1159 @@ +// +using System; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Tgstation.Server.Host.Database.Migrations +{ + [DbContext(typeof(MySqlDatabaseContext))] + [Migration("20240701235015_MYAddCronAutoUpdates")] + partial class MYAddCronAutoUpdates + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.6") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ChannelLimit") + .IsRequired() + .HasColumnType("smallint unsigned"); + + b.Property("ConnectionString") + .IsRequired() + .HasMaxLength(10000) + .HasColumnType("longtext"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("ConnectionString"), "utf8mb4"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Provider") + .HasColumnType("int"); + + b.Property("ReconnectionInterval") + .IsRequired() + .HasColumnType("int unsigned"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId", "Name") + .IsUnique(); + + b.ToTable("ChatBots"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ChatSettingsId") + .HasColumnType("bigint"); + + b.Property("DiscordChannelId") + .HasColumnType("bigint unsigned"); + + b.Property("IrcChannel") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("IrcChannel"), "utf8mb4"); + + b.Property("IsAdminChannel") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("IsSystemChannel") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("IsUpdatesChannel") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("IsWatchdogChannel") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("Tag") + .HasMaxLength(10000) + .HasColumnType("longtext"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("Tag"), "utf8mb4"); + + b.HasKey("Id"); + + b.HasIndex("ChatSettingsId", "DiscordChannelId") + .IsUnique(); + + b.HasIndex("ChatSettingsId", "IrcChannel") + .IsUnique(); + + b.ToTable("ChatChannels"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("DMApiMajorVersion") + .HasColumnType("int"); + + b.Property("DMApiMinorVersion") + .HasColumnType("int"); + + b.Property("DMApiPatchVersion") + .HasColumnType("int"); + + b.Property("DirectoryName") + .IsRequired() + .HasColumnType("char(36)"); + + b.Property("DmeName") + .IsRequired() + .HasColumnType("longtext"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("DmeName"), "utf8mb4"); + + b.Property("EngineVersion") + .IsRequired() + .HasColumnType("longtext"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("EngineVersion"), "utf8mb4"); + + b.Property("GitHubDeploymentId") + .HasColumnType("bigint"); + + b.Property("GitHubRepoId") + .HasColumnType("bigint"); + + b.Property("JobId") + .HasColumnType("bigint"); + + b.Property("MinimumSecurityLevel") + .HasColumnType("int"); + + b.Property("Output") + .IsRequired() + .HasColumnType("longtext"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("Output"), "utf8mb4"); + + b.Property("RepositoryOrigin") + .HasColumnType("longtext"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("RepositoryOrigin"), "utf8mb4"); + + b.Property("RevisionInformationId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("DirectoryName"); + + b.HasIndex("JobId") + .IsUnique(); + + b.HasIndex("RevisionInformationId"); + + b.ToTable("CompileJobs"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AdditionalParameters") + .IsRequired() + .HasMaxLength(10000) + .HasColumnType("longtext"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("AdditionalParameters"), "utf8mb4"); + + b.Property("AllowWebClient") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("AutoStart") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("DumpOnHealthCheckRestart") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("HealthCheckSeconds") + .IsRequired() + .HasColumnType("int unsigned"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("LogOutput") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("MapThreads") + .IsRequired() + .HasColumnType("int unsigned"); + + b.Property("Minidumps") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("Port") + .IsRequired() + .HasColumnType("smallint unsigned"); + + b.Property("SecurityLevel") + .HasColumnType("int"); + + b.Property("StartProfiler") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("StartupTimeout") + .IsRequired() + .HasColumnType("int unsigned"); + + b.Property("TopicRequestTimeout") + .IsRequired() + .HasColumnType("int unsigned"); + + b.Property("Visibility") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("DreamDaemonSettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ApiValidationPort") + .IsRequired() + .HasColumnType("smallint unsigned"); + + b.Property("ApiValidationSecurityLevel") + .HasColumnType("int"); + + b.Property("CompilerAdditionalArguments") + .HasMaxLength(10000) + .HasColumnType("varchar(10000)"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("ProjectName") + .HasMaxLength(10000) + .HasColumnType("longtext"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("ProjectName"), "utf8mb4"); + + b.Property("RequireDMApiValidation") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("Timeout") + .IsRequired() + .HasColumnType("time(6)"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("DreamMakerSettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AutoUpdateCron") + .IsRequired() + .HasMaxLength(10000) + .HasColumnType("varchar(10000)"); + + b.Property("AutoUpdateInterval") + .IsRequired() + .HasColumnType("int unsigned"); + + b.Property("ChatBotLimit") + .IsRequired() + .HasColumnType("smallint unsigned"); + + b.Property("ConfigurationType") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("Name"), "utf8mb4"); + + b.Property("Online") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("Path") + .IsRequired() + .HasColumnType("varchar(255)"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("Path"), "utf8mb4"); + + b.Property("SwarmIdentifer") + .HasColumnType("varchar(255)"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("SwarmIdentifer"), "utf8mb4"); + + b.HasKey("Id"); + + b.HasIndex("Path", "SwarmIdentifer") + .IsUnique(); + + b.ToTable("Instances"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.InstancePermissionSet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ChatBotRights") + .HasColumnType("bigint unsigned"); + + b.Property("ConfigurationRights") + .HasColumnType("bigint unsigned"); + + b.Property("DreamDaemonRights") + .HasColumnType("bigint unsigned"); + + b.Property("DreamMakerRights") + .HasColumnType("bigint unsigned"); + + b.Property("EngineRights") + .HasColumnType("bigint unsigned"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("InstancePermissionSetRights") + .HasColumnType("bigint unsigned"); + + b.Property("PermissionSetId") + .HasColumnType("bigint"); + + b.Property("RepositoryRights") + .HasColumnType("bigint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId"); + + b.HasIndex("PermissionSetId", "InstanceId") + .IsUnique(); + + b.ToTable("InstancePermissionSets"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CancelRight") + .HasColumnType("bigint unsigned"); + + b.Property("CancelRightsType") + .HasColumnType("bigint unsigned"); + + b.Property("Cancelled") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("CancelledById") + .HasColumnType("bigint"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("Description"), "utf8mb4"); + + b.Property("ErrorCode") + .HasColumnType("int unsigned"); + + b.Property("ExceptionDetails") + .HasColumnType("longtext"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("ExceptionDetails"), "utf8mb4"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("JobCode") + .HasColumnType("tinyint unsigned"); + + b.Property("StartedAt") + .IsRequired() + .HasColumnType("datetime(6)"); + + b.Property("StartedById") + .HasColumnType("bigint"); + + b.Property("StoppedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("CancelledById"); + + b.HasIndex("InstanceId"); + + b.HasIndex("StartedById"); + + b.ToTable("Jobs"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ExternalUserId") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("ExternalUserId"), "utf8mb4"); + + b.Property("Provider") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("Provider", "ExternalUserId") + .IsUnique(); + + b.ToTable("OAuthConnections"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.PermissionSet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AdministrationRights") + .HasColumnType("bigint unsigned"); + + b.Property("GroupId") + .HasColumnType("bigint"); + + b.Property("InstanceManagerRights") + .HasColumnType("bigint unsigned"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("GroupId") + .IsUnique(); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("PermissionSets"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AccessIdentifier") + .IsRequired() + .HasColumnType("longtext"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("AccessIdentifier"), "utf8mb4"); + + b.Property("CompileJobId") + .HasColumnType("bigint"); + + b.Property("InitialCompileJobId") + .HasColumnType("bigint"); + + b.Property("LaunchSecurityLevel") + .HasColumnType("int"); + + b.Property("LaunchVisibility") + .HasColumnType("int"); + + b.Property("Port") + .HasColumnType("smallint unsigned"); + + b.Property("ProcessId") + .HasColumnType("int"); + + b.Property("RebootState") + .HasColumnType("int"); + + b.Property("TopicPort") + .HasColumnType("smallint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("CompileJobId"); + + b.HasIndex("InitialCompileJobId"); + + b.ToTable("ReattachInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AccessToken") + .HasMaxLength(10000) + .HasColumnType("longtext"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("AccessToken"), "utf8mb4"); + + b.Property("AccessUser") + .HasMaxLength(10000) + .HasColumnType("longtext"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("AccessUser"), "utf8mb4"); + + b.Property("AutoUpdatesKeepTestMerges") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("AutoUpdatesSynchronize") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("CommitterEmail") + .IsRequired() + .HasMaxLength(10000) + .HasColumnType("longtext"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("CommitterEmail"), "utf8mb4"); + + b.Property("CommitterName") + .IsRequired() + .HasMaxLength(10000) + .HasColumnType("longtext"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("CommitterName"), "utf8mb4"); + + b.Property("CreateGitHubDeployments") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("PostTestMergeComment") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("PushTestMergeCommits") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("ShowTestMergeCommitters") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("UpdateSubmodules") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("RepositorySettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("RevisionInformationId") + .HasColumnType("bigint"); + + b.Property("TestMergeId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("RevisionInformationId"); + + b.HasIndex("TestMergeId"); + + b.ToTable("RevInfoTestMerges"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CommitSha") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("CommitSha"), "utf8mb4"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("OriginCommitSha") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("OriginCommitSha"), "utf8mb4"); + + b.Property("Timestamp") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId", "CommitSha") + .IsUnique(); + + b.ToTable("RevisionInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Author") + .IsRequired() + .HasColumnType("longtext"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("Author"), "utf8mb4"); + + b.Property("BodyAtMerge") + .IsRequired() + .HasColumnType("longtext"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("BodyAtMerge"), "utf8mb4"); + + b.Property("Comment") + .HasMaxLength(10000) + .HasColumnType("longtext"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("Comment"), "utf8mb4"); + + b.Property("MergedAt") + .HasColumnType("datetime(6)"); + + b.Property("MergedById") + .HasColumnType("bigint"); + + b.Property("Number") + .HasColumnType("int"); + + b.Property("PrimaryRevisionInformationId") + .IsRequired() + .HasColumnType("bigint"); + + b.Property("TargetCommitSha") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("TargetCommitSha"), "utf8mb4"); + + b.Property("TitleAtMerge") + .IsRequired() + .HasColumnType("longtext"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("TitleAtMerge"), "utf8mb4"); + + b.Property("Url") + .IsRequired() + .HasColumnType("longtext"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("Url"), "utf8mb4"); + + b.HasKey("Id"); + + b.HasIndex("MergedById"); + + b.HasIndex("PrimaryRevisionInformationId") + .IsUnique(); + + b.ToTable("TestMerges"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CanonicalName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("CanonicalName"), "utf8mb4"); + + b.Property("CreatedAt") + .IsRequired() + .HasColumnType("datetime(6)"); + + b.Property("CreatedById") + .HasColumnType("bigint"); + + b.Property("Enabled") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("GroupId") + .HasColumnType("bigint"); + + b.Property("LastPasswordUpdate") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("Name"), "utf8mb4"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("PasswordHash"), "utf8mb4"); + + b.Property("SystemIdentifier") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("SystemIdentifier"), "utf8mb4"); + + b.HasKey("Id"); + + b.HasIndex("CanonicalName") + .IsUnique(); + + b.HasIndex("CreatedById"); + + b.HasIndex("GroupId"); + + b.HasIndex("SystemIdentifier") + .IsUnique(); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.UserGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("Name"), "utf8mb4"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("Groups"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("ChatSettings") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Instance"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b => + { + b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings") + .WithMany("Channels") + .HasForeignKey("ChatSettingsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChatSettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b => + { + b.HasOne("Tgstation.Server.Host.Models.Job", "Job") + .WithOne() + .HasForeignKey("Tgstation.Server.Host.Models.CompileJob", "JobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation") + .WithMany("CompileJobs") + .HasForeignKey("RevisionInformationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Job"); + + b.Navigation("RevisionInformation"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithOne("DreamDaemonSettings") + .HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Instance"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithOne("DreamMakerSettings") + .HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Instance"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.InstancePermissionSet", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("InstancePermissionSets") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.PermissionSet", "PermissionSet") + .WithMany("InstancePermissionSets") + .HasForeignKey("PermissionSetId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Instance"); + + b.Navigation("PermissionSet"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy") + .WithMany() + .HasForeignKey("CancelledById"); + + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("Jobs") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy") + .WithMany() + .HasForeignKey("StartedById") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CancelledBy"); + + b.Navigation("Instance"); + + b.Navigation("StartedBy"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "User") + .WithMany("OAuthConnections") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.PermissionSet", b => + { + b.HasOne("Tgstation.Server.Host.Models.UserGroup", "Group") + .WithOne("PermissionSet") + .HasForeignKey("Tgstation.Server.Host.Models.PermissionSet", "GroupId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Tgstation.Server.Host.Models.User", "User") + .WithOne("PermissionSet") + .HasForeignKey("Tgstation.Server.Host.Models.PermissionSet", "UserId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Group"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b => + { + b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob") + .WithMany() + .HasForeignKey("CompileJobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.CompileJob", "InitialCompileJob") + .WithMany() + .HasForeignKey("InitialCompileJobId"); + + b.Navigation("CompileJob"); + + b.Navigation("InitialCompileJob"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithOne("RepositorySettings") + .HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Instance"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b => + { + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation") + .WithMany("ActiveTestMerges") + .HasForeignKey("RevisionInformationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge") + .WithMany("RevisonInformations") + .HasForeignKey("TestMergeId") + .OnDelete(DeleteBehavior.ClientNoAction) + .IsRequired(); + + b.Navigation("RevisionInformation"); + + b.Navigation("TestMerge"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("RevisionInformations") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Instance"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy") + .WithMany("TestMerges") + .HasForeignKey("MergedById") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation") + .WithOne("PrimaryTestMerge") + .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MergedBy"); + + b.Navigation("PrimaryRevisionInformation"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.User", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy") + .WithMany("CreatedUsers") + .HasForeignKey("CreatedById"); + + b.HasOne("Tgstation.Server.Host.Models.UserGroup", "Group") + .WithMany("Users") + .HasForeignKey("GroupId"); + + b.Navigation("CreatedBy"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => + { + b.Navigation("Channels"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b => + { + b.Navigation("ChatSettings"); + + b.Navigation("DreamDaemonSettings"); + + b.Navigation("DreamMakerSettings"); + + b.Navigation("InstancePermissionSets"); + + b.Navigation("Jobs"); + + b.Navigation("RepositorySettings"); + + b.Navigation("RevisionInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.PermissionSet", b => + { + b.Navigation("InstancePermissionSets"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b => + { + b.Navigation("ActiveTestMerges"); + + b.Navigation("CompileJobs"); + + b.Navigation("PrimaryTestMerge"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b => + { + b.Navigation("RevisonInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.User", b => + { + b.Navigation("CreatedUsers"); + + b.Navigation("OAuthConnections"); + + b.Navigation("PermissionSet"); + + b.Navigation("TestMerges"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.UserGroup", b => + { + b.Navigation("PermissionSet") + .IsRequired(); + + b.Navigation("Users"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Tgstation.Server.Host/Database/Migrations/20240701235015_MYAddCronAutoUpdates.cs b/src/Tgstation.Server.Host/Database/Migrations/20240701235015_MYAddCronAutoUpdates.cs new file mode 100644 index 0000000000..96e6a80021 --- /dev/null +++ b/src/Tgstation.Server.Host/Database/Migrations/20240701235015_MYAddCronAutoUpdates.cs @@ -0,0 +1,35 @@ +using System; + +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Tgstation.Server.Host.Database.Migrations +{ + /// + public partial class MYAddCronAutoUpdates : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + ArgumentNullException.ThrowIfNull(migrationBuilder); + + migrationBuilder.AddColumn( + name: "AutoUpdateCron", + table: "Instances", + type: "varchar(10000)", + maxLength: 10000, + nullable: false, + defaultValue: String.Empty) + .Annotation("MySql:CharSet", "utf8mb4"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + ArgumentNullException.ThrowIfNull(migrationBuilder); + + migrationBuilder.DropColumn( + name: "AutoUpdateCron", + table: "Instances"); + } + } +} diff --git a/src/Tgstation.Server.Host/Database/Migrations/20240701235023_PGAddCronAutoUpdates.Designer.cs b/src/Tgstation.Server.Host/Database/Migrations/20240701235023_PGAddCronAutoUpdates.Designer.cs new file mode 100644 index 0000000000..9fd7068c99 --- /dev/null +++ b/src/Tgstation.Server.Host/Database/Migrations/20240701235023_PGAddCronAutoUpdates.Designer.cs @@ -0,0 +1,1083 @@ +// +using System; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Tgstation.Server.Host.Database.Migrations +{ + [DbContext(typeof(PostgresSqlDatabaseContext))] + [Migration("20240701235023_PGAddCronAutoUpdates")] + partial class PGAddCronAutoUpdates + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.6") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ChannelLimit") + .HasColumnType("integer"); + + b.Property("ConnectionString") + .IsRequired() + .HasMaxLength(10000) + .HasColumnType("character varying(10000)"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Provider") + .HasColumnType("integer"); + + b.Property("ReconnectionInterval") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId", "Name") + .IsUnique(); + + b.ToTable("ChatBots"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ChatSettingsId") + .HasColumnType("bigint"); + + b.Property("DiscordChannelId") + .HasColumnType("numeric(20,0)"); + + b.Property("IrcChannel") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("IsAdminChannel") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("IsSystemChannel") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("IsUpdatesChannel") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("IsWatchdogChannel") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("Tag") + .HasMaxLength(10000) + .HasColumnType("character varying(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() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("DMApiMajorVersion") + .HasColumnType("integer"); + + b.Property("DMApiMinorVersion") + .HasColumnType("integer"); + + b.Property("DMApiPatchVersion") + .HasColumnType("integer"); + + b.Property("DirectoryName") + .IsRequired() + .HasColumnType("uuid"); + + b.Property("DmeName") + .IsRequired() + .HasColumnType("text"); + + b.Property("EngineVersion") + .IsRequired() + .HasColumnType("text"); + + b.Property("GitHubDeploymentId") + .HasColumnType("bigint"); + + b.Property("GitHubRepoId") + .HasColumnType("bigint"); + + b.Property("JobId") + .HasColumnType("bigint"); + + b.Property("MinimumSecurityLevel") + .HasColumnType("integer"); + + b.Property("Output") + .IsRequired() + .HasColumnType("text"); + + b.Property("RepositoryOrigin") + .HasColumnType("text"); + + b.Property("RevisionInformationId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("DirectoryName"); + + b.HasIndex("JobId") + .IsUnique(); + + b.HasIndex("RevisionInformationId"); + + b.ToTable("CompileJobs"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AdditionalParameters") + .IsRequired() + .HasMaxLength(10000) + .HasColumnType("character varying(10000)"); + + b.Property("AllowWebClient") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("AutoStart") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("DumpOnHealthCheckRestart") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("HealthCheckSeconds") + .HasColumnType("bigint"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("LogOutput") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("MapThreads") + .HasColumnType("bigint"); + + b.Property("Minidumps") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("Port") + .HasColumnType("integer"); + + b.Property("SecurityLevel") + .HasColumnType("integer"); + + b.Property("StartProfiler") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("StartupTimeout") + .HasColumnType("bigint"); + + b.Property("TopicRequestTimeout") + .HasColumnType("bigint"); + + b.Property("Visibility") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("DreamDaemonSettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ApiValidationPort") + .HasColumnType("integer"); + + b.Property("ApiValidationSecurityLevel") + .HasColumnType("integer"); + + b.Property("CompilerAdditionalArguments") + .HasMaxLength(10000) + .HasColumnType("character varying(10000)"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("ProjectName") + .HasMaxLength(10000) + .HasColumnType("character varying(10000)"); + + b.Property("RequireDMApiValidation") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("Timeout") + .IsRequired() + .HasColumnType("interval"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("DreamMakerSettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AutoUpdateCron") + .IsRequired() + .HasMaxLength(10000) + .HasColumnType("character varying(10000)"); + + b.Property("AutoUpdateInterval") + .HasColumnType("bigint"); + + b.Property("ChatBotLimit") + .HasColumnType("integer"); + + b.Property("ConfigurationType") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Online") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("Path") + .IsRequired() + .HasColumnType("text"); + + b.Property("SwarmIdentifer") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Path", "SwarmIdentifer") + .IsUnique(); + + b.ToTable("Instances"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.InstancePermissionSet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ChatBotRights") + .HasColumnType("numeric(20,0)"); + + b.Property("ConfigurationRights") + .HasColumnType("numeric(20,0)"); + + b.Property("DreamDaemonRights") + .HasColumnType("numeric(20,0)"); + + b.Property("DreamMakerRights") + .HasColumnType("numeric(20,0)"); + + b.Property("EngineRights") + .HasColumnType("numeric(20,0)"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("InstancePermissionSetRights") + .HasColumnType("numeric(20,0)"); + + b.Property("PermissionSetId") + .HasColumnType("bigint"); + + b.Property("RepositoryRights") + .HasColumnType("numeric(20,0)"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId"); + + b.HasIndex("PermissionSetId", "InstanceId") + .IsUnique(); + + b.ToTable("InstancePermissionSets"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CancelRight") + .HasColumnType("numeric(20,0)"); + + b.Property("CancelRightsType") + .HasColumnType("numeric(20,0)"); + + b.Property("Cancelled") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("CancelledById") + .HasColumnType("bigint"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("ErrorCode") + .HasColumnType("bigint"); + + b.Property("ExceptionDetails") + .HasColumnType("text"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("JobCode") + .HasColumnType("smallint"); + + b.Property("StartedAt") + .IsRequired() + .HasColumnType("timestamp with time zone"); + + b.Property("StartedById") + .HasColumnType("bigint"); + + b.Property("StoppedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("CancelledById"); + + b.HasIndex("InstanceId"); + + b.HasIndex("StartedById"); + + b.ToTable("Jobs"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ExternalUserId") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Provider") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("Provider", "ExternalUserId") + .IsUnique(); + + b.ToTable("OAuthConnections"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.PermissionSet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AdministrationRights") + .HasColumnType("numeric(20,0)"); + + b.Property("GroupId") + .HasColumnType("bigint"); + + b.Property("InstanceManagerRights") + .HasColumnType("numeric(20,0)"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("GroupId") + .IsUnique(); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("PermissionSets"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AccessIdentifier") + .IsRequired() + .HasColumnType("text"); + + b.Property("CompileJobId") + .HasColumnType("bigint"); + + b.Property("InitialCompileJobId") + .HasColumnType("bigint"); + + b.Property("LaunchSecurityLevel") + .HasColumnType("integer"); + + b.Property("LaunchVisibility") + .HasColumnType("integer"); + + b.Property("Port") + .HasColumnType("integer"); + + b.Property("ProcessId") + .HasColumnType("integer"); + + b.Property("RebootState") + .HasColumnType("integer"); + + b.Property("TopicPort") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("CompileJobId"); + + b.HasIndex("InitialCompileJobId"); + + b.ToTable("ReattachInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AccessToken") + .HasMaxLength(10000) + .HasColumnType("character varying(10000)"); + + b.Property("AccessUser") + .HasMaxLength(10000) + .HasColumnType("character varying(10000)"); + + b.Property("AutoUpdatesKeepTestMerges") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("AutoUpdatesSynchronize") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("CommitterEmail") + .IsRequired() + .HasMaxLength(10000) + .HasColumnType("character varying(10000)"); + + b.Property("CommitterName") + .IsRequired() + .HasMaxLength(10000) + .HasColumnType("character varying(10000)"); + + b.Property("CreateGitHubDeployments") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("PostTestMergeComment") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("PushTestMergeCommits") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("ShowTestMergeCommitters") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("UpdateSubmodules") + .IsRequired() + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("RepositorySettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("RevisionInformationId") + .HasColumnType("bigint"); + + b.Property("TestMergeId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("RevisionInformationId"); + + b.HasIndex("TestMergeId"); + + b.ToTable("RevInfoTestMerges"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CommitSha") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("OriginCommitSha") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId", "CommitSha") + .IsUnique(); + + b.ToTable("RevisionInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Author") + .IsRequired() + .HasColumnType("text"); + + b.Property("BodyAtMerge") + .IsRequired() + .HasColumnType("text"); + + b.Property("Comment") + .HasMaxLength(10000) + .HasColumnType("character varying(10000)"); + + b.Property("MergedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("MergedById") + .HasColumnType("bigint"); + + b.Property("Number") + .HasColumnType("integer"); + + b.Property("PrimaryRevisionInformationId") + .IsRequired() + .HasColumnType("bigint"); + + b.Property("TargetCommitSha") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("TitleAtMerge") + .IsRequired() + .HasColumnType("text"); + + b.Property("Url") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("MergedById"); + + b.HasIndex("PrimaryRevisionInformationId") + .IsUnique(); + + b.ToTable("TestMerges"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CanonicalName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedAt") + .IsRequired() + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedById") + .HasColumnType("bigint"); + + b.Property("Enabled") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("GroupId") + .HasColumnType("bigint"); + + b.Property("LastPasswordUpdate") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("SystemIdentifier") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("CanonicalName") + .IsUnique(); + + b.HasIndex("CreatedById"); + + b.HasIndex("GroupId"); + + b.HasIndex("SystemIdentifier") + .IsUnique(); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.UserGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("Groups"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("ChatSettings") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Instance"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b => + { + b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings") + .WithMany("Channels") + .HasForeignKey("ChatSettingsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChatSettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b => + { + b.HasOne("Tgstation.Server.Host.Models.Job", "Job") + .WithOne() + .HasForeignKey("Tgstation.Server.Host.Models.CompileJob", "JobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation") + .WithMany("CompileJobs") + .HasForeignKey("RevisionInformationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Job"); + + b.Navigation("RevisionInformation"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithOne("DreamDaemonSettings") + .HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Instance"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithOne("DreamMakerSettings") + .HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Instance"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.InstancePermissionSet", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("InstancePermissionSets") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.PermissionSet", "PermissionSet") + .WithMany("InstancePermissionSets") + .HasForeignKey("PermissionSetId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Instance"); + + b.Navigation("PermissionSet"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy") + .WithMany() + .HasForeignKey("CancelledById"); + + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("Jobs") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy") + .WithMany() + .HasForeignKey("StartedById") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CancelledBy"); + + b.Navigation("Instance"); + + b.Navigation("StartedBy"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "User") + .WithMany("OAuthConnections") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.PermissionSet", b => + { + b.HasOne("Tgstation.Server.Host.Models.UserGroup", "Group") + .WithOne("PermissionSet") + .HasForeignKey("Tgstation.Server.Host.Models.PermissionSet", "GroupId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Tgstation.Server.Host.Models.User", "User") + .WithOne("PermissionSet") + .HasForeignKey("Tgstation.Server.Host.Models.PermissionSet", "UserId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Group"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b => + { + b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob") + .WithMany() + .HasForeignKey("CompileJobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.CompileJob", "InitialCompileJob") + .WithMany() + .HasForeignKey("InitialCompileJobId"); + + b.Navigation("CompileJob"); + + b.Navigation("InitialCompileJob"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithOne("RepositorySettings") + .HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Instance"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b => + { + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation") + .WithMany("ActiveTestMerges") + .HasForeignKey("RevisionInformationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge") + .WithMany("RevisonInformations") + .HasForeignKey("TestMergeId") + .OnDelete(DeleteBehavior.ClientNoAction) + .IsRequired(); + + b.Navigation("RevisionInformation"); + + b.Navigation("TestMerge"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("RevisionInformations") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Instance"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy") + .WithMany("TestMerges") + .HasForeignKey("MergedById") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation") + .WithOne("PrimaryTestMerge") + .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MergedBy"); + + b.Navigation("PrimaryRevisionInformation"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.User", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy") + .WithMany("CreatedUsers") + .HasForeignKey("CreatedById"); + + b.HasOne("Tgstation.Server.Host.Models.UserGroup", "Group") + .WithMany("Users") + .HasForeignKey("GroupId"); + + b.Navigation("CreatedBy"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => + { + b.Navigation("Channels"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b => + { + b.Navigation("ChatSettings"); + + b.Navigation("DreamDaemonSettings"); + + b.Navigation("DreamMakerSettings"); + + b.Navigation("InstancePermissionSets"); + + b.Navigation("Jobs"); + + b.Navigation("RepositorySettings"); + + b.Navigation("RevisionInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.PermissionSet", b => + { + b.Navigation("InstancePermissionSets"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b => + { + b.Navigation("ActiveTestMerges"); + + b.Navigation("CompileJobs"); + + b.Navigation("PrimaryTestMerge"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b => + { + b.Navigation("RevisonInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.User", b => + { + b.Navigation("CreatedUsers"); + + b.Navigation("OAuthConnections"); + + b.Navigation("PermissionSet"); + + b.Navigation("TestMerges"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.UserGroup", b => + { + b.Navigation("PermissionSet") + .IsRequired(); + + b.Navigation("Users"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Tgstation.Server.Host/Database/Migrations/20240701235023_PGAddCronAutoUpdates.cs b/src/Tgstation.Server.Host/Database/Migrations/20240701235023_PGAddCronAutoUpdates.cs new file mode 100644 index 0000000000..9cc9706f0a --- /dev/null +++ b/src/Tgstation.Server.Host/Database/Migrations/20240701235023_PGAddCronAutoUpdates.cs @@ -0,0 +1,34 @@ +using System; + +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Tgstation.Server.Host.Database.Migrations +{ + /// + public partial class PGAddCronAutoUpdates : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + ArgumentNullException.ThrowIfNull(migrationBuilder); + + migrationBuilder.AddColumn( + name: "AutoUpdateCron", + table: "Instances", + type: "character varying(10000)", + maxLength: 10000, + nullable: false, + defaultValue: String.Empty); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + ArgumentNullException.ThrowIfNull(migrationBuilder); + + migrationBuilder.DropColumn( + name: "AutoUpdateCron", + table: "Instances"); + } + } +} diff --git a/src/Tgstation.Server.Host/Database/Migrations/20240701235031_SLAddCronAutoUpdates.Designer.cs b/src/Tgstation.Server.Host/Database/Migrations/20240701235031_SLAddCronAutoUpdates.Designer.cs new file mode 100644 index 0000000000..4f3740059b --- /dev/null +++ b/src/Tgstation.Server.Host/Database/Migrations/20240701235031_SLAddCronAutoUpdates.Designer.cs @@ -0,0 +1,1055 @@ +// +using System; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Tgstation.Server.Host.Database.Migrations +{ + [DbContext(typeof(SqliteDatabaseContext))] + [Migration("20240701235031_SLAddCronAutoUpdates")] + partial class SLAddCronAutoUpdates + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.6"); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChannelLimit") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("ConnectionString") + .IsRequired() + .HasMaxLength(10000) + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("InstanceId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("Provider") + .HasColumnType("INTEGER"); + + b.Property("ReconnectionInterval") + .IsRequired() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId", "Name") + .IsUnique(); + + b.ToTable("ChatBots"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChatSettingsId") + .HasColumnType("INTEGER"); + + b.Property("DiscordChannelId") + .HasColumnType("INTEGER"); + + b.Property("IrcChannel") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("IsAdminChannel") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("IsSystemChannel") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("IsUpdatesChannel") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("IsWatchdogChannel") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("Tag") + .HasMaxLength(10000) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ChatSettingsId", "DiscordChannelId") + .IsUnique(); + + b.HasIndex("ChatSettingsId", "IrcChannel") + .IsUnique(); + + b.ToTable("ChatChannels"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DMApiMajorVersion") + .HasColumnType("INTEGER"); + + b.Property("DMApiMinorVersion") + .HasColumnType("INTEGER"); + + b.Property("DMApiPatchVersion") + .HasColumnType("INTEGER"); + + b.Property("DirectoryName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DmeName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("EngineVersion") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("GitHubDeploymentId") + .HasColumnType("INTEGER"); + + b.Property("GitHubRepoId") + .HasColumnType("INTEGER"); + + b.Property("JobId") + .HasColumnType("INTEGER"); + + b.Property("MinimumSecurityLevel") + .HasColumnType("INTEGER"); + + b.Property("Output") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RepositoryOrigin") + .HasColumnType("TEXT"); + + b.Property("RevisionInformationId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("DirectoryName"); + + b.HasIndex("JobId") + .IsUnique(); + + b.HasIndex("RevisionInformationId"); + + b.ToTable("CompileJobs"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AdditionalParameters") + .IsRequired() + .HasMaxLength(10000) + .HasColumnType("TEXT"); + + b.Property("AllowWebClient") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("AutoStart") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("DumpOnHealthCheckRestart") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("HealthCheckSeconds") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("InstanceId") + .HasColumnType("INTEGER"); + + b.Property("LogOutput") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("MapThreads") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("Minidumps") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("Port") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("SecurityLevel") + .HasColumnType("INTEGER"); + + b.Property("StartProfiler") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("StartupTimeout") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("TopicRequestTimeout") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("Visibility") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("DreamDaemonSettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ApiValidationPort") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("ApiValidationSecurityLevel") + .HasColumnType("INTEGER"); + + b.Property("CompilerAdditionalArguments") + .HasMaxLength(10000) + .HasColumnType("TEXT"); + + b.Property("InstanceId") + .HasColumnType("INTEGER"); + + b.Property("ProjectName") + .HasMaxLength(10000) + .HasColumnType("TEXT"); + + b.Property("RequireDMApiValidation") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("Timeout") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("DreamMakerSettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AutoUpdateCron") + .IsRequired() + .HasMaxLength(10000) + .HasColumnType("TEXT"); + + b.Property("AutoUpdateInterval") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("ChatBotLimit") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("ConfigurationType") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("Online") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("Path") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("SwarmIdentifer") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Path", "SwarmIdentifer") + .IsUnique(); + + b.ToTable("Instances"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.InstancePermissionSet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChatBotRights") + .HasColumnType("INTEGER"); + + b.Property("ConfigurationRights") + .HasColumnType("INTEGER"); + + b.Property("DreamDaemonRights") + .HasColumnType("INTEGER"); + + b.Property("DreamMakerRights") + .HasColumnType("INTEGER"); + + b.Property("EngineRights") + .HasColumnType("INTEGER"); + + b.Property("InstanceId") + .HasColumnType("INTEGER"); + + b.Property("InstancePermissionSetRights") + .HasColumnType("INTEGER"); + + b.Property("PermissionSetId") + .HasColumnType("INTEGER"); + + b.Property("RepositoryRights") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId"); + + b.HasIndex("PermissionSetId", "InstanceId") + .IsUnique(); + + b.ToTable("InstancePermissionSets"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CancelRight") + .HasColumnType("INTEGER"); + + b.Property("CancelRightsType") + .HasColumnType("INTEGER"); + + b.Property("Cancelled") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("CancelledById") + .HasColumnType("INTEGER"); + + b.Property("Description") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ErrorCode") + .HasColumnType("INTEGER"); + + b.Property("ExceptionDetails") + .HasColumnType("TEXT"); + + b.Property("InstanceId") + .HasColumnType("INTEGER"); + + b.Property("JobCode") + .HasColumnType("INTEGER"); + + b.Property("StartedAt") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("StartedById") + .HasColumnType("INTEGER"); + + b.Property("StoppedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CancelledById"); + + b.HasIndex("InstanceId"); + + b.HasIndex("StartedById"); + + b.ToTable("Jobs"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ExternalUserId") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("Provider") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("Provider", "ExternalUserId") + .IsUnique(); + + b.ToTable("OAuthConnections"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.PermissionSet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AdministrationRights") + .HasColumnType("INTEGER"); + + b.Property("GroupId") + .HasColumnType("INTEGER"); + + b.Property("InstanceManagerRights") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("GroupId") + .IsUnique(); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("PermissionSets"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AccessIdentifier") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CompileJobId") + .HasColumnType("INTEGER"); + + b.Property("InitialCompileJobId") + .HasColumnType("INTEGER"); + + b.Property("LaunchSecurityLevel") + .HasColumnType("INTEGER"); + + b.Property("LaunchVisibility") + .HasColumnType("INTEGER"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("ProcessId") + .HasColumnType("INTEGER"); + + b.Property("RebootState") + .HasColumnType("INTEGER"); + + b.Property("TopicPort") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CompileJobId"); + + b.HasIndex("InitialCompileJobId"); + + b.ToTable("ReattachInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AccessToken") + .HasMaxLength(10000) + .HasColumnType("TEXT"); + + b.Property("AccessUser") + .HasMaxLength(10000) + .HasColumnType("TEXT"); + + b.Property("AutoUpdatesKeepTestMerges") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("AutoUpdatesSynchronize") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("CommitterEmail") + .IsRequired() + .HasMaxLength(10000) + .HasColumnType("TEXT"); + + b.Property("CommitterName") + .IsRequired() + .HasMaxLength(10000) + .HasColumnType("TEXT"); + + b.Property("CreateGitHubDeployments") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("InstanceId") + .HasColumnType("INTEGER"); + + b.Property("PostTestMergeComment") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("PushTestMergeCommits") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("ShowTestMergeCommitters") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("UpdateSubmodules") + .IsRequired() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("RepositorySettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("RevisionInformationId") + .HasColumnType("INTEGER"); + + b.Property("TestMergeId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("RevisionInformationId"); + + b.HasIndex("TestMergeId"); + + b.ToTable("RevInfoTestMerges"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CommitSha") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("TEXT"); + + b.Property("InstanceId") + .HasColumnType("INTEGER"); + + b.Property("OriginCommitSha") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("TEXT"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId", "CommitSha") + .IsUnique(); + + b.ToTable("RevisionInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Author") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("BodyAtMerge") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Comment") + .HasMaxLength(10000) + .HasColumnType("TEXT"); + + b.Property("MergedAt") + .HasColumnType("TEXT"); + + b.Property("MergedById") + .HasColumnType("INTEGER"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("PrimaryRevisionInformationId") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("TargetCommitSha") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("TEXT"); + + b.Property("TitleAtMerge") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Url") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MergedById"); + + b.HasIndex("PrimaryRevisionInformationId") + .IsUnique(); + + b.ToTable("TestMerges"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CanonicalName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedById") + .HasColumnType("INTEGER"); + + b.Property("Enabled") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("GroupId") + .HasColumnType("INTEGER"); + + b.Property("LastPasswordUpdate") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("PasswordHash") + .HasColumnType("TEXT"); + + b.Property("SystemIdentifier") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CanonicalName") + .IsUnique(); + + b.HasIndex("CreatedById"); + + b.HasIndex("GroupId"); + + b.HasIndex("SystemIdentifier") + .IsUnique(); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.UserGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("Groups"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("ChatSettings") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Instance"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b => + { + b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings") + .WithMany("Channels") + .HasForeignKey("ChatSettingsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChatSettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b => + { + b.HasOne("Tgstation.Server.Host.Models.Job", "Job") + .WithOne() + .HasForeignKey("Tgstation.Server.Host.Models.CompileJob", "JobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation") + .WithMany("CompileJobs") + .HasForeignKey("RevisionInformationId") + .OnDelete(DeleteBehavior.ClientNoAction) + .IsRequired(); + + b.Navigation("Job"); + + b.Navigation("RevisionInformation"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithOne("DreamDaemonSettings") + .HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Instance"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithOne("DreamMakerSettings") + .HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Instance"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.InstancePermissionSet", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("InstancePermissionSets") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.PermissionSet", "PermissionSet") + .WithMany("InstancePermissionSets") + .HasForeignKey("PermissionSetId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Instance"); + + b.Navigation("PermissionSet"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy") + .WithMany() + .HasForeignKey("CancelledById"); + + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("Jobs") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy") + .WithMany() + .HasForeignKey("StartedById") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CancelledBy"); + + b.Navigation("Instance"); + + b.Navigation("StartedBy"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "User") + .WithMany("OAuthConnections") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.PermissionSet", b => + { + b.HasOne("Tgstation.Server.Host.Models.UserGroup", "Group") + .WithOne("PermissionSet") + .HasForeignKey("Tgstation.Server.Host.Models.PermissionSet", "GroupId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Tgstation.Server.Host.Models.User", "User") + .WithOne("PermissionSet") + .HasForeignKey("Tgstation.Server.Host.Models.PermissionSet", "UserId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Group"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b => + { + b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob") + .WithMany() + .HasForeignKey("CompileJobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.CompileJob", "InitialCompileJob") + .WithMany() + .HasForeignKey("InitialCompileJobId"); + + b.Navigation("CompileJob"); + + b.Navigation("InitialCompileJob"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithOne("RepositorySettings") + .HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Instance"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b => + { + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation") + .WithMany("ActiveTestMerges") + .HasForeignKey("RevisionInformationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge") + .WithMany("RevisonInformations") + .HasForeignKey("TestMergeId") + .OnDelete(DeleteBehavior.ClientNoAction) + .IsRequired(); + + b.Navigation("RevisionInformation"); + + b.Navigation("TestMerge"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("RevisionInformations") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Instance"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy") + .WithMany("TestMerges") + .HasForeignKey("MergedById") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation") + .WithOne("PrimaryTestMerge") + .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MergedBy"); + + b.Navigation("PrimaryRevisionInformation"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.User", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy") + .WithMany("CreatedUsers") + .HasForeignKey("CreatedById"); + + b.HasOne("Tgstation.Server.Host.Models.UserGroup", "Group") + .WithMany("Users") + .HasForeignKey("GroupId"); + + b.Navigation("CreatedBy"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => + { + b.Navigation("Channels"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b => + { + b.Navigation("ChatSettings"); + + b.Navigation("DreamDaemonSettings"); + + b.Navigation("DreamMakerSettings"); + + b.Navigation("InstancePermissionSets"); + + b.Navigation("Jobs"); + + b.Navigation("RepositorySettings"); + + b.Navigation("RevisionInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.PermissionSet", b => + { + b.Navigation("InstancePermissionSets"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b => + { + b.Navigation("ActiveTestMerges"); + + b.Navigation("CompileJobs"); + + b.Navigation("PrimaryTestMerge"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b => + { + b.Navigation("RevisonInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.User", b => + { + b.Navigation("CreatedUsers"); + + b.Navigation("OAuthConnections"); + + b.Navigation("PermissionSet"); + + b.Navigation("TestMerges"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.UserGroup", b => + { + b.Navigation("PermissionSet") + .IsRequired(); + + b.Navigation("Users"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Tgstation.Server.Host/Database/Migrations/20240701235031_SLAddCronAutoUpdates.cs b/src/Tgstation.Server.Host/Database/Migrations/20240701235031_SLAddCronAutoUpdates.cs new file mode 100644 index 0000000000..994dc781de --- /dev/null +++ b/src/Tgstation.Server.Host/Database/Migrations/20240701235031_SLAddCronAutoUpdates.cs @@ -0,0 +1,34 @@ +using System; + +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Tgstation.Server.Host.Database.Migrations +{ + /// + public partial class SLAddCronAutoUpdates : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + ArgumentNullException.ThrowIfNull(migrationBuilder); + + migrationBuilder.AddColumn( + name: "AutoUpdateCron", + table: "Instances", + type: "TEXT", + maxLength: 10000, + nullable: false, + defaultValue: String.Empty); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + ArgumentNullException.ThrowIfNull(migrationBuilder); + + migrationBuilder.DropColumn( + name: "AutoUpdateCron", + table: "Instances"); + } + } +} diff --git a/src/Tgstation.Server.Host/Database/Migrations/MySqlDatabaseContextModelSnapshot.cs b/src/Tgstation.Server.Host/Database/Migrations/MySqlDatabaseContextModelSnapshot.cs index 38615ad916..9e6ee7fd0d 100644 --- a/src/Tgstation.Server.Host/Database/Migrations/MySqlDatabaseContextModelSnapshot.cs +++ b/src/Tgstation.Server.Host/Database/Migrations/MySqlDatabaseContextModelSnapshot.cs @@ -315,6 +315,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + b.Property("AutoUpdateCron") + .IsRequired() + .HasMaxLength(10000) + .HasColumnType("varchar(10000)"); + b.Property("AutoUpdateInterval") .IsRequired() .HasColumnType("int unsigned"); diff --git a/src/Tgstation.Server.Host/Database/Migrations/PostgresSqlDatabaseContextModelSnapshot.cs b/src/Tgstation.Server.Host/Database/Migrations/PostgresSqlDatabaseContextModelSnapshot.cs index 357a8ed8e0..de0a215df4 100644 --- a/src/Tgstation.Server.Host/Database/Migrations/PostgresSqlDatabaseContextModelSnapshot.cs +++ b/src/Tgstation.Server.Host/Database/Migrations/PostgresSqlDatabaseContextModelSnapshot.cs @@ -289,6 +289,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + b.Property("AutoUpdateCron") + .IsRequired() + .HasMaxLength(10000) + .HasColumnType("character varying(10000)"); + b.Property("AutoUpdateInterval") .HasColumnType("bigint"); diff --git a/src/Tgstation.Server.Host/Database/Migrations/SqlServerDatabaseContextModelSnapshot.cs b/src/Tgstation.Server.Host/Database/Migrations/SqlServerDatabaseContextModelSnapshot.cs index 60f1d2201e..e3722f96a2 100644 --- a/src/Tgstation.Server.Host/Database/Migrations/SqlServerDatabaseContextModelSnapshot.cs +++ b/src/Tgstation.Server.Host/Database/Migrations/SqlServerDatabaseContextModelSnapshot.cs @@ -291,6 +291,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + b.Property("AutoUpdateCron") + .IsRequired() + .HasMaxLength(10000) + .HasColumnType("nvarchar(max)"); + b.Property("AutoUpdateInterval") .HasColumnType("bigint"); diff --git a/src/Tgstation.Server.Host/Database/Migrations/SqliteDatabaseContextModelSnapshot.cs b/src/Tgstation.Server.Host/Database/Migrations/SqliteDatabaseContextModelSnapshot.cs index 3af9f50ca5..2ec5601598 100644 --- a/src/Tgstation.Server.Host/Database/Migrations/SqliteDatabaseContextModelSnapshot.cs +++ b/src/Tgstation.Server.Host/Database/Migrations/SqliteDatabaseContextModelSnapshot.cs @@ -12,7 +12,7 @@ partial class SqliteDatabaseContextModelSnapshot : ModelSnapshot protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "8.0.4"); + modelBuilder.HasAnnotation("ProductVersion", "8.0.6"); modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => { @@ -129,7 +129,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("TEXT"); - b.Property("GitHubDeploymentId") + b.Property("GitHubDeploymentId") .HasColumnType("INTEGER"); b.Property("GitHubRepoId") @@ -281,6 +281,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) .ValueGeneratedOnAdd() .HasColumnType("INTEGER"); + b.Property("AutoUpdateCron") + .IsRequired() + .HasMaxLength(10000) + .HasColumnType("TEXT"); + b.Property("AutoUpdateInterval") .IsRequired() .HasColumnType("INTEGER"); diff --git a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj index 4c361c9c45..9c5dbd6fef 100644 --- a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj +++ b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj @@ -97,6 +97,7 @@ + diff --git a/tests/Tgstation.Server.Tests/Live/InstanceManagerTest.cs b/tests/Tgstation.Server.Tests/Live/InstanceManagerTest.cs index 776c4322d3..21442827b6 100644 --- a/tests/Tgstation.Server.Tests/Live/InstanceManagerTest.cs +++ b/tests/Tgstation.Server.Tests/Live/InstanceManagerTest.cs @@ -162,6 +162,22 @@ public async Task RunPreTest(CancellationToken cancellationToken) Assert.AreEqual(ConfigurationType.HostWrite, firstTest.ConfigurationType); Assert.IsTrue(Directory.Exists(firstTest.Path)); + // a couple data validation checks + // check setting both fails + await ApiAssert.ThrowsException(() => instanceManagerClient.Update(new InstanceUpdateRequest + { + Id = firstTest.Id, + AutoUpdateInterval = 9999, + AutoUpdateCron = "0 0 0 1 1 *" + }, cancellationToken), ErrorCode.ModelValidationFailure); + + // check bad crons fail + await ApiAssert.ThrowsException(() => instanceManagerClient.Update(new InstanceUpdateRequest + { + Id = firstTest.Id, + AutoUpdateCron = "not a cron" + }, cancellationToken), ErrorCode.ModelValidationFailure); + // regression check await instanceManagerClient.Update(new InstanceUpdateRequest { @@ -169,6 +185,13 @@ public async Task RunPreTest(CancellationToken cancellationToken) AutoUpdateInterval = 9999, }, cancellationToken); + // check regular 6-part crons succeed + await instanceManagerClient.Update(new InstanceUpdateRequest + { + Id = firstTest.Id, + AutoUpdateCron = "0 0 0 1 1 *" + }, cancellationToken); + //can't move online instance await ApiAssert.ThrowsException(() => instanceManagerClient.Update(new InstanceUpdateRequest { diff --git a/tests/Tgstation.Server.Tests/TestDatabase.cs b/tests/Tgstation.Server.Tests/TestDatabase.cs index c5b4b13f50..94db53f46e 100644 --- a/tests/Tgstation.Server.Tests/TestDatabase.cs +++ b/tests/Tgstation.Server.Tests/TestDatabase.cs @@ -104,6 +104,7 @@ DatabaseContext CreateContext() var instance = new Host.Models.Instance { AutoUpdateInterval = 0, + AutoUpdateCron = String.Empty, ChatBotLimit = 1, ChatSettings = new List(), ConfigurationType = ConfigurationType.HostWrite,