diff --git a/src/Tgstation.Server.Api/Models/Internal/Job.cs b/src/Tgstation.Server.Api/Models/Internal/Job.cs
index 7738cf0043..b4247221e1 100644
--- a/src/Tgstation.Server.Api/Models/Internal/Job.cs
+++ b/src/Tgstation.Server.Api/Models/Internal/Job.cs
@@ -33,7 +33,8 @@ namespace Tgstation.Server.Api.Models.Internal
/// When the was started
///
[Permissions(DenyWrite = true)]
- public DateTimeOffset StartedAt { get; set; }
+ [Required]
+ public DateTimeOffset? StartedAt { get; set; }
///
/// When the stopped
@@ -45,7 +46,8 @@ namespace Tgstation.Server.Api.Models.Internal
/// If the was cancelled
///
[Permissions(DenyWrite = true)]
- public bool Cancelled { get; set; }
+ [Required]
+ public bool? Cancelled { get; set; }
///
/// The of if it can be cancelled
diff --git a/src/Tgstation.Server.Host/Components/Byond/WindowsByondInstaller.cs b/src/Tgstation.Server.Host/Components/Byond/WindowsByondInstaller.cs
index 41ae7207d3..3e5422c62d 100644
--- a/src/Tgstation.Server.Host/Components/Byond/WindowsByondInstaller.cs
+++ b/src/Tgstation.Server.Host/Components/Byond/WindowsByondInstaller.cs
@@ -5,6 +5,7 @@ using System.Globalization;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
+using Tgstation.Server.Host.Core;
using Tgstation.Server.Host.IO;
namespace Tgstation.Server.Host.Components.Byond
@@ -12,7 +13,7 @@ namespace Tgstation.Server.Host.Components.Byond
///
/// for windows systems
///
- sealed class WindowsByondInstaller : IByondInstaller
+ sealed class WindowsByondInstaller : IByondInstaller, IDisposable
{
///
/// The URL format string for getting BYOND windows version {0}.{1} zipfile
@@ -51,6 +52,11 @@ namespace Tgstation.Server.Host.Components.Byond
///
readonly ILogger logger;
+ ///
+ /// The for the
+ ///
+ readonly SemaphoreSlim semaphore;
+
///
/// If DirectX was installed
///
@@ -66,9 +72,13 @@ namespace Tgstation.Server.Host.Components.Byond
this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
+ semaphore = new SemaphoreSlim(1);
installedDirectX = false;
}
+ ///
+ public void Dispose() => semaphore.Dispose();
+
///
public async Task CleanCache(CancellationToken cancellationToken)
{
@@ -103,8 +113,8 @@ namespace Tgstation.Server.Host.Components.Byond
var setNoPromptTrustedModeTask = SetNoPromptTrusted();
//after this version lummox made DD depend of directx lol
- if (version.Major >= 512 && version.Minor >= 1427 && Monitor.TryEnter(this))
- try
+ if (version.Major >= 512 && version.Minor >= 1427 && !installedDirectX)
+ using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
{
if (!installedDirectX)
//always install it, it's pretty fast and will do better redundancy checking than us
@@ -143,10 +153,6 @@ namespace Tgstation.Server.Host.Components.Byond
installedDirectX = true;
}
}
- finally
- {
- Monitor.Exit(this);
- }
await setNoPromptTrustedModeTask.ConfigureAwait(false);
}
diff --git a/src/Tgstation.Server.Host/Components/Compiler/DmbFactory.cs b/src/Tgstation.Server.Host/Components/Compiler/DmbFactory.cs
index 08fffc4f0a..e994fddfae 100644
--- a/src/Tgstation.Server.Host/Components/Compiler/DmbFactory.cs
+++ b/src/Tgstation.Server.Host/Components/Compiler/DmbFactory.cs
@@ -107,7 +107,7 @@ namespace Tgstation.Server.Host.Components.Compiler
{
if (job == null)
throw new ArgumentNullException(nameof(job));
- if (job.DMApiValidated != true || job.Job.Cancelled || job.Job.ExceptionDetails != null || job.Job.StoppedAt == null)
+ if (job.DMApiValidated != true || job.Job.Cancelled.Value || job.Job.ExceptionDetails != null || job.Job.StoppedAt == null)
throw new InvalidOperationException("Cannot load incomplete compile job!");
if (setAsStagedInDb)
await databaseContextFactory.UseContext(async db =>
diff --git a/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs b/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs
index d25f914517..c9acbb0d6a 100644
--- a/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs
+++ b/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs
@@ -3,7 +3,6 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;
-using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Tgstation.Server.Api.Rights;
@@ -73,25 +72,6 @@ namespace Tgstation.Server.Host.Controllers
return Json(job);
}
- ///
- [TgsAuthorize(DreamMakerRights.CancelCompile)]
- public override async Task Delete(long id, CancellationToken cancellationToken)
- {
- //alias for cancelling the latest job
- var job = await DatabaseContext.CompileJobs.OrderByDescending(x => x.Job.StartedAt).Select(x => new Job { Id = x.Job.Id, StoppedAt = x.Job.StoppedAt }).FirstAsync(cancellationToken).ConfigureAwait(false);
- if (job.StoppedAt != null)
- return StatusCode((int)HttpStatusCode.Gone);
- try
- {
- await jobManager.CancelJob(job, AuthenticationContext.User, cancellationToken).ConfigureAwait(false);
- }
- catch (InvalidOperationException) //job already stopped
- {
- return StatusCode((int)HttpStatusCode.Gone);
- }
- return Ok();
- }
-
///
[TgsAuthorize(DreamMakerRights.SetDme)]
public override async Task Update([FromBody] Api.Models.DreamMaker model, CancellationToken cancellationToken)
diff --git a/src/Tgstation.Server.Host/Controllers/ModelController.cs b/src/Tgstation.Server.Host/Controllers/ModelController.cs
index 69680526fe..c9ba839646 100644
--- a/src/Tgstation.Server.Host/Controllers/ModelController.cs
+++ b/src/Tgstation.Server.Host/Controllers/ModelController.cs
@@ -68,7 +68,7 @@ namespace Tgstation.Server.Host.Controllers
/// The ID of the model to delete
/// The for the operation
/// A resulting in the of the operation
- [HttpDelete]
+ [HttpDelete("{id}")]
public virtual Task Delete(long id, CancellationToken cancellationToken) => Task.FromResult((IActionResult)NotFound());
///
diff --git a/src/Tgstation.Server.Host/Core/JobManager.cs b/src/Tgstation.Server.Host/Core/JobManager.cs
index c72590d5c9..4d8359e438 100644
--- a/src/Tgstation.Server.Host/Core/JobManager.cs
+++ b/src/Tgstation.Server.Host/Core/JobManager.cs
@@ -10,7 +10,7 @@ using Tgstation.Server.Host.Models;
namespace Tgstation.Server.Host.Core
{
///
- sealed class JobManager : IHostedService, IJobManager
+ sealed class JobManager : IHostedService, IJobManager, IDisposable
{
///
/// The for the
@@ -31,6 +31,13 @@ namespace Tgstation.Server.Host.Core
jobs = new Dictionary();
}
+ ///
+ public void Dispose()
+ {
+ foreach (var job in jobs)
+ job.Value.Dispose();
+ }
+
///
/// Gets the for a given if it exists
///
@@ -104,6 +111,7 @@ namespace Tgstation.Server.Host.Core
{
var databaseContext = scope.ServiceProvider.GetRequiredService();
job.StartedAt = DateTimeOffset.Now;
+ job.Cancelled = false;
job.Instance = new Instance
{
Id = job.Instance.Id
@@ -134,7 +142,7 @@ namespace Tgstation.Server.Host.Core
await databaseContext.Initialize(cancellationToken).ConfigureAwait(false);
//mark all jobs as cancelled
- var enumerator = await databaseContext.Jobs.Where(y => !y.Cancelled && y.StoppedAt == null).Select(y => y.Id).ToAsyncEnumerable().ToList(cancellationToken).ConfigureAwait(false);
+ var enumerator = await databaseContext.Jobs.Where(y => !y.Cancelled.Value && !y.StoppedAt.HasValue).Select(y => y.Id).ToAsyncEnumerable().ToList(cancellationToken).ConfigureAwait(false);
foreach(var I in enumerator)
{
var job = new Job { Id = I };
@@ -154,9 +162,6 @@ namespace Tgstation.Server.Host.Core
return x.Value.Wait(cancellationToken);
});
await Task.WhenAll(joinTasks).ConfigureAwait(false);
- foreach (var job in jobs)
- job.Value.Dispose();
- jobs.Clear();
}
///
@@ -175,6 +180,7 @@ namespace Tgstation.Server.Host.Core
user = new User { Id = user.Id };
databaseContext.Users.Attach(user);
job.CancelledBy = user;
+ //let either startup or cancellation set job.cancelled
await databaseContext.Save(cancellationToken).ConfigureAwait(false);
}
}
diff --git a/src/Tgstation.Server.Host/IO/DefaultIOManager.cs b/src/Tgstation.Server.Host/IO/DefaultIOManager.cs
index 865411b1b6..c4b235b3d7 100644
--- a/src/Tgstation.Server.Host/IO/DefaultIOManager.cs
+++ b/src/Tgstation.Server.Host/IO/DefaultIOManager.cs
@@ -265,9 +265,27 @@ namespace Tgstation.Server.Host.IO
///
public async Task DownloadFile(Uri url, CancellationToken cancellationToken)
{
+ //DownloadDataTaskAsync can't be cancelled and is shittily written, don't use it
using (var wc = new WebClient())
- using (cancellationToken.Register(() => wc.CancelAsync()))
- return await wc.DownloadDataTaskAsync(url).ConfigureAwait(false);
+ {
+ var tcs = new TaskCompletionSource();
+ wc.DownloadDataCompleted += (a, b) =>
+ {
+ if (b.Error != null)
+ tcs.TrySetException(b.Error);
+ else if (b.Cancelled)
+ tcs.TrySetCanceled();
+ else
+ tcs.TrySetResult(b.Result);
+ };
+ wc.DownloadDataAsync(url);
+ using (cancellationToken.Register(() =>
+ {
+ wc.CancelAsync(); //cancelasync alone doesnt do it either! who wrote this!!
+ tcs.SetCanceled();
+ }))
+ return await tcs.Task.ConfigureAwait(false);
+ } //ITS STILL FUCKING DOWNLOADING!!!
}
///
diff --git a/tools/TGS.postman_collection.json b/tools/TGS.postman_collection.json
index d3692a376d..92ea78f73b 100644
--- a/tools/TGS.postman_collection.json
+++ b/tools/TGS.postman_collection.json
@@ -842,6 +842,49 @@
},
"response": []
},
+ {
+ "name": "Set ID 1 to only #botbus",
+ "request": {
+ "method": "POST",
+ "header": [
+ {
+ "key": "Accept",
+ "value": "application/json"
+ },
+ {
+ "key": "User-Agent",
+ "value": "Postman/1.0"
+ },
+ {
+ "key": "User-Agent",
+ "value": "Tgstation.Server.Api/4.0.0.0"
+ },
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ },
+ {
+ "key": "InstanceId",
+ "value": "1"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n\t\"id\": 1,\n\t\"channels\": [\n\t\t{\n\t\t\t\"ircChannel\": \"#botbus\",\n\t\t\t\"isAdminChannel\": true,\n\t\t\t\"isWatchdogChannel\": true\n\t\t}\n\t\t]\n}"
+ },
+ "url": {
+ "raw": "localhost:5000/Chat",
+ "host": [
+ "localhost"
+ ],
+ "port": "5000",
+ "path": [
+ "Chat"
+ ]
+ }
+ },
+ "response": []
+ },
{
"name": "Create r4407 on Disord",
"request": {
@@ -1199,6 +1242,167 @@
],
"_postman_isSubFolder": true
},
+ {
+ "name": "Jobs",
+ "description": "",
+ "item": [
+ {
+ "name": "List Ids",
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Accept",
+ "value": "application/json"
+ },
+ {
+ "key": "User-Agent",
+ "value": "Postman/1.0"
+ },
+ {
+ "key": "User-Agent",
+ "value": "Tgstation.Server.Api/4.0.0.0"
+ },
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ },
+ {
+ "key": "InstanceId",
+ "value": "1"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n\t\"userId\": 1,\n\t\"byondRights\": -1,\n\t\"dreamDaemonRights\": -1,\n\t\"dreamMakerRights\": -1,\n\t\"repositoryRights\": -1,\n\t\"chatSettingsRights\": -1,\n\t\"configurationRights\": -1\n}"
+ },
+ "url": {
+ "raw": "localhost:5000/Job/List",
+ "host": [
+ "localhost"
+ ],
+ "port": "5000",
+ "path": [
+ "Job",
+ "List"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Get ID",
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Accept",
+ "value": "application/json"
+ },
+ {
+ "key": "User-Agent",
+ "value": "Postman/1.0"
+ },
+ {
+ "key": "User-Agent",
+ "value": "Tgstation.Server.Api/4.0.0.0"
+ },
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ },
+ {
+ "key": "InstanceId",
+ "value": "1"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n\t\"userId\": 1,\n\t\"byondRights\": -1,\n\t\"dreamDaemonRights\": -1,\n\t\"dreamMakerRights\": -1,\n\t\"repositoryRights\": -1,\n\t\"chatSettingsRights\": -1,\n\t\"configurationRights\": -1\n}"
+ },
+ "url": {
+ "raw": "localhost:5000/Job/10",
+ "host": [
+ "localhost"
+ ],
+ "port": "5000",
+ "path": [
+ "Job",
+ "10"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Cancel ID",
+ "request": {
+ "method": "DELETE",
+ "header": [
+ {
+ "key": "Accept",
+ "value": "application/json"
+ },
+ {
+ "key": "User-Agent",
+ "value": "Postman/1.0"
+ },
+ {
+ "key": "User-Agent",
+ "value": "Tgstation.Server.Api/4.0.0.0"
+ },
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ },
+ {
+ "key": "InstanceId",
+ "value": "1"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": ""
+ },
+ "url": {
+ "raw": "localhost:5000/Job/11",
+ "host": [
+ "localhost"
+ ],
+ "port": "5000",
+ "path": [
+ "Job",
+ "11"
+ ]
+ }
+ },
+ "response": []
+ }
+ ],
+ "event": [
+ {
+ "listen": "prerequest",
+ "script": {
+ "id": "a3f3a2f7-5700-48a9-88bf-d18ac2a1855c",
+ "type": "text/javascript",
+ "exec": [
+ ""
+ ]
+ }
+ },
+ {
+ "listen": "test",
+ "script": {
+ "id": "787a5a17-ad9c-4772-9052-85d81e87d4a9",
+ "type": "text/javascript",
+ "exec": [
+ ""
+ ]
+ }
+ }
+ ],
+ "_postman_isSubFolder": true
+ },
{
"name": "Online Instance ID 1",
"request": {
@@ -1397,6 +1601,185 @@
}
]
},
+ {
+ "name": "Byond",
+ "description": "",
+ "item": [
+ {
+ "name": "List installed versions",
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Accept",
+ "value": "application/json"
+ },
+ {
+ "key": "User-Agent",
+ "value": "Postman/1.0"
+ },
+ {
+ "key": "User-Agent",
+ "value": "Tgstation.Server.Api/4.0.0.0"
+ },
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ },
+ {
+ "key": "InstanceId",
+ "value": "1"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"id\": 1,\n \"online\": false\n}"
+ },
+ "url": {
+ "raw": "localhost:5000/Byond/List",
+ "host": [
+ "localhost"
+ ],
+ "port": "5000",
+ "path": [
+ "Byond",
+ "List"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Read active version",
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Accept",
+ "value": "application/json"
+ },
+ {
+ "key": "User-Agent",
+ "value": "Postman/1.0"
+ },
+ {
+ "key": "User-Agent",
+ "value": "Tgstation.Server.Api/4.0.0.0"
+ },
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ },
+ {
+ "key": "InstanceId",
+ "value": "1"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"id\": 1,\n \"online\": false\n}"
+ },
+ "url": {
+ "raw": "localhost:5000/Byond",
+ "host": [
+ "localhost"
+ ],
+ "port": "5000",
+ "path": [
+ "Byond"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Set 511.1385 active",
+ "request": {
+ "method": "POST",
+ "header": [
+ {
+ "key": "Accept",
+ "value": "application/json"
+ },
+ {
+ "key": "User-Agent",
+ "value": "Postman/1.0"
+ },
+ {
+ "key": "User-Agent",
+ "value": "Tgstation.Server.Api/4.0.0.0"
+ },
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ },
+ {
+ "key": "InstanceId",
+ "value": "1"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"version\": {\n \t\"major\": 511,\n \t\"minor\": 1385\n }\n}"
+ },
+ "url": {
+ "raw": "localhost:5000/Byond",
+ "host": [
+ "localhost"
+ ],
+ "port": "5000",
+ "path": [
+ "Byond"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Set 512.1441 active",
+ "request": {
+ "method": "POST",
+ "header": [
+ {
+ "key": "Accept",
+ "value": "application/json"
+ },
+ {
+ "key": "User-Agent",
+ "value": "Postman/1.0"
+ },
+ {
+ "key": "User-Agent",
+ "value": "Tgstation.Server.Api/4.0.0.0"
+ },
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ },
+ {
+ "key": "InstanceId",
+ "value": "1"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"version\": {\n \t\"major\": 512,\n \t\"minor\": 1441\n }\n}"
+ },
+ "url": {
+ "raw": "localhost:5000/Byond",
+ "host": [
+ "localhost"
+ ],
+ "port": "5000",
+ "path": [
+ "Byond"
+ ]
+ }
+ },
+ "response": []
+ }
+ ]
+ },
{
"name": "Login Admin Default",
"request": {