diff --git a/build/Version.props b/build/Version.props index b65405fdb7..d72259794c 100644 --- a/build/Version.props +++ b/build/Version.props @@ -4,7 +4,7 @@ 4.5.0 2.0.0 - 7.2.0 + 7.3.0 8.2.0 5.2.3 0.4.0 diff --git a/src/Tgstation.Server.Api/Models/ErrorCode.cs b/src/Tgstation.Server.Api/Models/ErrorCode.cs index 174de92a6f..6c9772d582 100644 --- a/src/Tgstation.Server.Api/Models/ErrorCode.cs +++ b/src/Tgstation.Server.Api/Models/ErrorCode.cs @@ -563,6 +563,12 @@ namespace Tgstation.Server.Api.Models /// An attempt to connect a chat bot failed. /// [Description("Failed to connect chat bot!")] - ChatCannotConnectProvider + ChatCannotConnectProvider, + + /// + /// Attempt to add DreamDaemon to the list of firewall exempt processes failed. + /// + [Description("Failed to allow DreamDaemon through the Windows firewall!")] + ByondDreamDaemonFirewallFail, } } diff --git a/src/Tgstation.Server.Api/Models/LogFile.cs b/src/Tgstation.Server.Api/Models/LogFile.cs index cdb99832c0..3f67f93d11 100644 --- a/src/Tgstation.Server.Api/Models/LogFile.cs +++ b/src/Tgstation.Server.Api/Models/LogFile.cs @@ -1,4 +1,4 @@ -using System; +using System; using Tgstation.Server.Api.Models.Internal; namespace Tgstation.Server.Api.Models @@ -11,7 +11,7 @@ namespace Tgstation.Server.Api.Models /// /// The name of the log file. /// - public string? Name { get; set; } + public string Name { get; set; } = String.Empty; /// /// The of when the log file was modified. diff --git a/src/Tgstation.Server.Host/.config/dotnet-tools.json b/src/Tgstation.Server.Host/.config/dotnet-tools.json index 4947a30bdf..79d13146a3 100644 --- a/src/Tgstation.Server.Host/.config/dotnet-tools.json +++ b/src/Tgstation.Server.Host/.config/dotnet-tools.json @@ -3,10 +3,10 @@ "isRoot": true, "tools": { "dotnet-ef": { - "version": "3.1.6", + "version": "3.1.7", "commands": [ "dotnet-ef" ] } } -} \ No newline at end of file +} diff --git a/src/Tgstation.Server.Host/Components/Byond/WindowsByondInstaller.cs b/src/Tgstation.Server.Host/Components/Byond/WindowsByondInstaller.cs index eb249c99c7..12782bbb8b 100644 --- a/src/Tgstation.Server.Host/Components/Byond/WindowsByondInstaller.cs +++ b/src/Tgstation.Server.Host/Components/Byond/WindowsByondInstaller.cs @@ -1,4 +1,4 @@ -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging; using System; using System.Text; using System.Threading; @@ -84,65 +84,99 @@ namespace Tgstation.Server.Host.Components.Byond public void Dispose() => semaphore.Dispose(); /// - public override async Task InstallByond(string path, Version version, CancellationToken cancellationToken) - { - async Task SetNoPromptTrusted() - { - var configPath = IOManager.ConcatPath(path, ByondConfigDir); - await IOManager.CreateDirectory(configPath, cancellationToken).ConfigureAwait(false); + public override Task InstallByond(string path, Version version, CancellationToken cancellationToken) + => Task.WhenAll( + SetNoPromptTrusted(path, cancellationToken), + InstallDirectX(path, cancellationToken), + AddDreamDaemonToFirewall(path, cancellationToken)); - var configFilePath = IOManager.ConcatPath(configPath, ByondDDConfig); - Logger.LogTrace("Disabling trusted prompts in {0}...", configFilePath); - await IOManager.WriteAllBytes( - configFilePath, - Encoding.UTF8.GetBytes(ByondNoPromptTrustedMode), - cancellationToken) - .ConfigureAwait(false); + async Task SetNoPromptTrusted(string path, CancellationToken cancellationToken) + { + var configPath = IOManager.ConcatPath(path, ByondConfigDir); + await IOManager.CreateDirectory(configPath, cancellationToken).ConfigureAwait(false); + + var configFilePath = IOManager.ConcatPath(configPath, ByondDDConfig); + Logger.LogTrace("Disabling trusted prompts in {0}...", configFilePath); + await IOManager.WriteAllBytes( + configFilePath, + Encoding.UTF8.GetBytes(ByondNoPromptTrustedMode), + cancellationToken) + .ConfigureAwait(false); + } + + async Task InstallDirectX(string path, CancellationToken cancellationToken) + { + using var lockContext = await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false); + if (installedDirectX) + { + Logger.LogTrace("DirectX already installed."); + return; } - var setNoPromptTrustedModeTask = SetNoPromptTrusted(); + Logger.LogTrace("Installing DirectX redistributable..."); - // after this version lummox made DD depend of directx lol - // but then he became amazing and not only fixed it but also gave us 30s compiles \[T]/ - // then he readded it again so -_- - if (!installedDirectX) - using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false)) - if (!installedDirectX) - { - // ^check again because race conditions - Logger.LogTrace("Installing DirectX redistributable..."); + // always install it, it's pretty fast and will do better redundancy checking than us + var rbdx = IOManager.ConcatPath(path, ByondDXDir); - // always install it, it's pretty fast and will do better redundancy checking than us - var rbdx = IOManager.ConcatPath(path, ByondDXDir); + try + { + // noShellExecute because we aren't doing runas shennanigans + using var directXInstaller = processExecutor.LaunchProcess( + IOManager.ConcatPath(rbdx, "DXSETUP.exe"), + rbdx, + "/silent", + noShellExecute: true); - // noShellExecute because we aren't doing runas shennanigans - IProcess directXInstaller; - try - { - directXInstaller = processExecutor.LaunchProcess( - IOManager.ConcatPath(rbdx, "DXSETUP.exe"), - rbdx, "/silent", - noShellExecute: true); - } - catch (Exception e) - { - throw new JobException(ErrorCode.ByondDirectXInstallFail, e); - } + int exitCode; + using (cancellationToken.Register(() => directXInstaller.Terminate())) + exitCode = await directXInstaller.Lifetime.ConfigureAwait(false); + cancellationToken.ThrowIfCancellationRequested(); - using (directXInstaller) - { - int exitCode; - using (cancellationToken.Register(() => directXInstaller.Terminate())) - exitCode = await directXInstaller.Lifetime.ConfigureAwait(false); - cancellationToken.ThrowIfCancellationRequested(); + if (exitCode != 0) + throw new JobException(ErrorCode.ByondDirectXInstallFail, new JobException($"Invalid exit code: {exitCode}")); + installedDirectX = true; + } + catch (Exception e) + { + throw new JobException(ErrorCode.ByondDirectXInstallFail, e); + } + } - if (exitCode != 0) - throw new JobException(ErrorCode.ByondDirectXInstallFail, new JobException($"Invalid exit code: {exitCode}")); - installedDirectX = true; - } - } + async Task AddDreamDaemonToFirewall(string path, CancellationToken cancellationToken) + { + var dreamDaemonPath = IOManager.ResolvePath( + IOManager.ConcatPath( + path, + ByondManager.BinPath, + DreamDaemonName)); - await setNoPromptTrustedModeTask.ConfigureAwait(false); + try + { + using var netshProcess = processExecutor.LaunchProcess( + "netsh.exe", + IOManager.ResolvePath(), + $"advfirewall firewall add rule name=\"TGS DreamDaemon\" program=\"{dreamDaemonPath}\" protocol=tcp dir=in enable=yes action=allow", + true, + true, + true); + + int exitCode; + using (cancellationToken.Register(() => netshProcess.Terminate())) + exitCode = await netshProcess.Lifetime.ConfigureAwait(false); + cancellationToken.ThrowIfCancellationRequested(); + + Logger.LogDebug( + "netsh.exe output:{0}{1}", + Environment.NewLine, + await netshProcess.GetCombinedOutput(cancellationToken).ConfigureAwait(false)); + + if (exitCode != 0) + throw new JobException(ErrorCode.ByondDreamDaemonFirewallFail, new JobException($"Invalid exit code: {exitCode}")); + } + catch (Exception ex) + { + throw new JobException(ErrorCode.ByondDreamDaemonFirewallFail, ex); + } } } } diff --git a/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs b/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs index a660d927d7..e4b6aef02f 100644 --- a/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs +++ b/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs @@ -57,7 +57,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers /// /// The mention provided by the Discord library /// The normalized mention - static string NormalizeMentions(string fromDiscord) => fromDiscord.Replace(" fromDiscord.Replace("<@!", "<@", StringComparison.Ordinal); /// /// Construct a @@ -96,6 +96,16 @@ namespace Tgstation.Server.Host.Components.Chat.Providers if (e.Author.Id == client.CurrentUser.Id) return; + if (e.Content.Equals("Based on what?", StringComparison.OrdinalIgnoreCase)) + { + // DCT: None available + await SendMessage( + e.Channel.Id, + "https://youtu.be/LrNu-SuFF_o", + default) + .ConfigureAwait(false); + } + var pm = e.Channel is IPrivateChannel; if (!pm && !mappedChannels.Contains(e.Channel.Id)) diff --git a/src/Tgstation.Server.Host/Controllers/AdministrationController.cs b/src/Tgstation.Server.Host/Controllers/AdministrationController.cs index d689416fa9..bb68d12467 100644 --- a/src/Tgstation.Server.Host/Controllers/AdministrationController.cs +++ b/src/Tgstation.Server.Host/Controllers/AdministrationController.cs @@ -374,7 +374,7 @@ namespace Tgstation.Server.Host.Controllers /// An IO error occurred while downloading. [HttpGet(Routes.Logs + "/{*path}")] [TgsAuthorize(AdministrationRights.DownloadLogs)] - [ProducesResponseType(typeof(List), 200)] + [ProducesResponseType(typeof(LogFile), 200)] [ProducesResponseType(typeof(ErrorMessage), 409)] public async Task GetLog(string path, CancellationToken cancellationToken) { diff --git a/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs b/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs index cecd5f2c93..3a76b8bc5b 100644 --- a/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs +++ b/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs @@ -1,4 +1,4 @@ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; @@ -291,6 +291,9 @@ namespace Tgstation.Server.Host.Controllers if (directory == null) throw new ArgumentNullException(nameof(directory)); + if (directory.Path == null) + return BadRequest(new ErrorMessage(ErrorCode.ModelValidationFailure)); + if (ForbidDueToModeConflicts(directory.Path, out var systemIdentity)) return Forbid(); diff --git a/src/Tgstation.Server.Host/Core/SwaggerConfiguration.cs b/src/Tgstation.Server.Host/Core/SwaggerConfiguration.cs index 674ba28987..2266298ef6 100644 --- a/src/Tgstation.Server.Host/Core/SwaggerConfiguration.cs +++ b/src/Tgstation.Server.Host/Core/SwaggerConfiguration.cs @@ -128,7 +128,18 @@ namespace Tgstation.Server.Host.Core new OpenApiInfo { Title = "TGS API", - Version = ApiHeaders.Version.Semver().ToString() + Version = ApiHeaders.Version.Semver().ToString(), + License = new OpenApiLicense + { + Name = "AGPL-3.0", + Url = new Uri("https://github.com/tgstation/tgstation-server/blob/dev/LICENSE") + }, + Contact = new OpenApiContact + { + Name = "/tg/station 13", + Url = new Uri("https://github.com/tgstation") + }, + Description = "A production scale tool for BYOND server management" }); // Important to do this before applying our own filters @@ -136,6 +147,9 @@ namespace Tgstation.Server.Host.Core swaggerGenOptions.IncludeXmlComments(assemblyDocumentationPath); swaggerGenOptions.IncludeXmlComments(apiDocumentationPath); + // nullable stuff + swaggerGenOptions.UseAllOfToExtendReferenceSchemas(); + swaggerGenOptions.OperationFilter(); swaggerGenOptions.DocumentFilter(); swaggerGenOptions.SchemaFilter(); @@ -337,15 +351,18 @@ namespace Tgstation.Server.Host.Core // Nothing is required schema.Required.Clear(); - if (!schema.Enum?.Any() ?? false) - return; - // Could be nullable type, make sure to get the right one - Type enumType = context.Type.IsConstructedGenericType + Type nonNullableType = context.Type.IsConstructedGenericType ? context.Type.GenericTypeArguments.First() : context.Type; - OpenApiEnumVarNamesExtension.Apply(schema, enumType); + if (nonNullableType != context.Type) + schema.Nullable = true; + + if (!schema.Enum?.Any() ?? false) + return; + + OpenApiEnumVarNamesExtension.Apply(schema, nonNullableType); } } } diff --git a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj index ba065d5aa6..cad2b4d249 100644 --- a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj +++ b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj @@ -48,20 +48,20 @@ - - - + + + all runtime; build; native; contentfiles; analyzers - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -81,12 +81,12 @@ - + - +