diff --git a/src/Tgstation.Server.Api/Models/Internal/DreamDaemonLaunchParameters.cs b/src/Tgstation.Server.Api/Models/Internal/DreamDaemonLaunchParameters.cs
index 8602b302b5..62e0f7eb87 100644
--- a/src/Tgstation.Server.Api/Models/Internal/DreamDaemonLaunchParameters.cs
+++ b/src/Tgstation.Server.Api/Models/Internal/DreamDaemonLaunchParameters.cs
@@ -1,4 +1,5 @@
-using Tgstation.Server.Api.Rights;
+using System.ComponentModel.DataAnnotations;
+using Tgstation.Server.Api.Rights;
namespace Tgstation.Server.Api.Models.Internal
{
@@ -12,24 +13,28 @@ namespace Tgstation.Server.Api.Models.Internal
/// If the BYOND web client can be used to connect to the game server
///
[Permissions(ReadRight = DreamDaemonRights.ReadMetadata, WriteRight = DreamDaemonRights.SetWebClient)]
- public bool AllowWebClient { get; set; }
+ [Required]
+ public bool? AllowWebClient { get; set; }
///
/// The level of
///
[Permissions(ReadRight = DreamDaemonRights.ReadMetadata, WriteRight = DreamDaemonRights.SetSecurity)]
- public DreamDaemonSecurity SecurityLevel { get; set; }
+ [Required]
+ public DreamDaemonSecurity? SecurityLevel { get; set; }
///
/// The first port uses. This should be the publically advertised port
///
[Permissions(ReadRight = DreamDaemonRights.ReadMetadata, WriteRight = DreamDaemonRights.SetPorts)]
- public ushort PrimaryPort { get; set; }
+ [Required]
+ public ushort? PrimaryPort { get; set; }
///
/// The second port uses
///
[Permissions(ReadRight = DreamDaemonRights.ReadMetadata, WriteRight = DreamDaemonRights.SetPorts)]
- public ushort SecondaryPort { get; set; }
+ [Required]
+ public ushort? SecondaryPort { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Tgstation.Server.Api/Models/Internal/DreamDaemonSettings.cs b/src/Tgstation.Server.Api/Models/Internal/DreamDaemonSettings.cs
index b4fd279881..72260ee5fc 100644
--- a/src/Tgstation.Server.Api/Models/Internal/DreamDaemonSettings.cs
+++ b/src/Tgstation.Server.Api/Models/Internal/DreamDaemonSettings.cs
@@ -1,4 +1,5 @@
-using Tgstation.Server.Api.Rights;
+using System.ComponentModel.DataAnnotations;
+using Tgstation.Server.Api.Rights;
namespace Tgstation.Server.Api.Models.Internal
{
@@ -11,18 +12,21 @@ namespace Tgstation.Server.Api.Models.Internal
/// If starts when it's starts
///
[Permissions(ReadRight = DreamDaemonRights.ReadMetadata, WriteRight = DreamDaemonRights.SetAutoStart)]
- public bool AutoStart { get; set; }
+ [Required]
+ public bool? AutoStart { get; set; }
///
/// If the server is undergoing a soft reset. This may be automatically set by changes to other fields
///
[Permissions(ReadRight = DreamDaemonRights.ReadMetadata, WriteRight = DreamDaemonRights.SoftRestart)]
- public bool SoftRestart { get; set; }
+ [Required]
+ public bool? SoftRestart { get; set; }
///
/// If the server is undergoing a soft shutdown
///
[Permissions(ReadRight = DreamDaemonRights.ReadMetadata, WriteRight = DreamDaemonRights.SoftShutdown)]
- public bool SoftShutdown { get; set; }
+ [Required]
+ public bool? SoftShutdown { get; set; }
}
}
diff --git a/src/Tgstation.Server.Host/Components/IDreamDaemon.cs b/src/Tgstation.Server.Host/Components/IDreamDaemon.cs
index dbd32b9d2a..ae7dee5038 100644
--- a/src/Tgstation.Server.Host/Components/IDreamDaemon.cs
+++ b/src/Tgstation.Server.Host/Components/IDreamDaemon.cs
@@ -41,6 +41,10 @@ namespace Tgstation.Server.Host.Components
///
string AccessToken { get; }
+ DreamDaemonLaunchParameters LastLaunchParameters { get; }
+
+ Host.Models.CompileJob LastCompileJob { get; }
+
///
/// Launch DreamDaemon
///
diff --git a/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs b/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs
new file mode 100644
index 0000000000..d8f082abb6
--- /dev/null
+++ b/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs
@@ -0,0 +1,121 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
+using System;
+using System.Linq;
+using System.Net;
+using System.Threading;
+using System.Threading.Tasks;
+using Tgstation.Server.Api.Models;
+using Tgstation.Server.Api.Models.Internal;
+using Tgstation.Server.Api.Rights;
+using Tgstation.Server.Host.Components;
+using Tgstation.Server.Host.Core;
+using Tgstation.Server.Host.Models;
+using Tgstation.Server.Host.Security;
+
+namespace Tgstation.Server.Host.Controllers
+{
+ [Route("/" + nameof(DreamDaemon))]
+ public sealed class DreamDaemonController : ModelController
+ {
+ ///
+ /// The for the
+ ///
+ readonly IJobManager jobManager;
+ ///
+ /// The for the
+ ///
+ readonly IInstanceManager instanceManager;
+
+ ///
+ /// Construct a
+ ///
+ /// The for the
+ /// The for the
+ public DreamDaemonController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IJobManager jobManager, IInstanceManager instanceManager) : base(databaseContext, authenticationContextFactory)
+ {
+ this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
+ this.instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager));
+ }
+
+ ///
+ [TgsAuthorize(DreamDaemonRights.Start)]
+ public override async Task Create([FromBody] Api.Models.DreamDaemon model, CancellationToken cancellationToken)
+ {
+ var instance = instanceManager.GetInstance(Instance);
+
+ if (instance.DreamDaemon.Running)
+ return StatusCode(HttpStatusCode.Gone);
+
+ var launchParams = await DatabaseContext.Instances.Where(x => x.Id == Instance.Id).Select(x => new DreamDaemonLaunchParameters
+ {
+ AllowWebClient = x.DreamDaemonSettings.AllowWebClient,
+ PrimaryPort = x.DreamDaemonSettings.PrimaryPort,
+ SecondaryPort = x.DreamDaemonSettings.SecondaryPort,
+ SecurityLevel = x.DreamDaemonSettings.SecurityLevel
+ }).FirstAsync(cancellationToken).ConfigureAwait(false);
+
+ await jobManager.RegisterOperation(new Models.Job
+ {
+ Description = "Launch DreamDaemon",
+ CancelRight = (int)DreamDaemonRights.Shutdown,
+ CancelRightsType = RightsType.DreamDaemon,
+ Instance = Instance,
+ StartedBy = AuthenticationContext.User
+ }, (job, serviceProvider, innerCt) => instance.DreamDaemon.Launch(launchParams, innerCt), cancellationToken).ConfigureAwait(false);
+ return Ok();
+ }
+
+ ///
+ [TgsAuthorize(DreamDaemonRights.ReadMetadata | DreamDaemonRights.ReadRevision)]
+ public override async Task Read(CancellationToken cancellationToken)
+ {
+ var dd = instanceManager.GetInstance(Instance).DreamDaemon;
+
+ var metadata = (AuthenticationContext.GetRight(RightsType.DreamDaemon) & (int)DreamDaemonRights.ReadMetadata) != 0;
+ var revision = (AuthenticationContext.GetRight(RightsType.DreamDaemon) & (int)DreamDaemonRights.ReadRevision) != 0;
+
+ var settings = metadata ? await DatabaseContext.Instances.Where(x => x.Id == Instance.Id).Select(x => x.DreamDaemonSettings).FirstAsync(cancellationToken).ConfigureAwait(false) : null;
+ Api.Models.DreamDaemon result = new Api.Models.DreamDaemon();
+ if(metadata)
+ {
+ result.AutoStart = settings.AutoStart;
+ result.CurrentPort = dd.CurrentPort;
+ result.CurrentSecurity = dd.CurrentSecurity;
+ result.PrimaryPort = dd.LastLaunchParameters.PrimaryPort;
+ result.AllowWebClient = dd.LastLaunchParameters.AllowWebClient;
+ result.Running = dd.Running;
+ result.SecondaryPort = dd.LastLaunchParameters.SecondaryPort;
+ result.SecurityLevel = dd.LastLaunchParameters.SecurityLevel;
+ result.SoftRestart = dd.SoftRebooting;
+ result.SoftShutdown = dd.SoftStopping;
+ };
+ if (revision)
+ result.CompileJob = dd.LastCompileJob.ToApi();
+
+ return Json(result);
+ }
+
+ ///
+ [TgsAuthorize(DreamDaemonRights.Shutdown)]
+ public override async Task Delete([FromBody] Api.Models.DreamDaemon model, CancellationToken cancellationToken)
+ {
+ var instance = instanceManager.GetInstance(Instance);
+
+ if (!instance.DreamDaemon.Running)
+ return StatusCode(HttpStatusCode.Gone);
+
+ await instance.DreamDaemon.Terminate(false, cancellationToken).ConfigureAwait(false);
+ return Ok();
+ }
+
+ [TgsAuthorize(DreamDaemonRights.SetAutoStart | DreamDaemonRights.SetPorts | DreamDaemonRights.SetSecurity | DreamDaemonRights.SetWebClient | DreamDaemonRights.SoftRestart | DreamDaemonRights.SoftShutdown)]
+ public override async Task Update([FromBody] Api.Models.DreamDaemon model, CancellationToken cancellationToken)
+ {
+ var current = await DatabaseContext.Instances.Where(x => x.Id == Instance.Id).Select(x => x.DreamDaemonSettings).FirstAsync(cancellationToken).ConfigureAwait(false);
+
+ var userRights = (DreamDaemonRights)AuthenticationContext.GetRight(RightsType.DreamDaemon)
+ if()
+ }
+ }
+}
diff --git a/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs b/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs
index f94a51537e..c13cb31e11 100644
--- a/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs
+++ b/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs
@@ -17,7 +17,7 @@ namespace Tgstation.Server.Host.Controllers
///
/// Controller for managing the compiler
///
- [Route("/DreamMaker")]
+ [Route("/" + nameof(DreamMaker))]
public sealed class DreamMakerController : ModelController
{
///
@@ -30,7 +30,7 @@ namespace Tgstation.Server.Host.Controllers
readonly IInstanceManager instanceManager;
///
- /// Construct a
+ /// Construct a
///
/// The for the
/// The for the
@@ -66,7 +66,8 @@ namespace Tgstation.Server.Host.Controllers
Description = "Compile active repository code",
StartedBy = AuthenticationContext.User,
CancelRightsType = RightsType.DreamMaker,
- CancelRight = (int)DreamMakerRights.CancelCompile
+ CancelRight = (int)DreamMakerRights.CancelCompile,
+ Instance = Instance
};
await jobManager.RegisterOperation(job, (paramJob, serviceProvider, ct) => RunCompile(paramJob, serviceProvider, Instance, ct), cancellationToken).ConfigureAwait(false);
return Json(job);
diff --git a/src/Tgstation.Server.Host/Controllers/JobController.cs b/src/Tgstation.Server.Host/Controllers/JobController.cs
index 982fc72cd8..4be80670a2 100644
--- a/src/Tgstation.Server.Host/Controllers/JobController.cs
+++ b/src/Tgstation.Server.Host/Controllers/JobController.cs
@@ -14,7 +14,7 @@ namespace Tgstation.Server.Host.Controllers
///
/// for s
///
- [Route("/Job")]
+ [Route("/" + nameof(Job))]
public sealed class JobController : ModelController
{
///
@@ -60,11 +60,23 @@ namespace Tgstation.Server.Host.Controllers
if (job == default(Job))
return NotFound();
+ if (job.CancelRight.HasValue && job.CancelRightsType.HasValue && (AuthenticationContext.GetRight(job.CancelRightsType.Value) & job.CancelRight.Value) == 0)
+ return Forbid();
+
if(job.StoppedAt != null)
return StatusCode(HttpStatusCode.Gone);
await jobManager.CancelJob(job, AuthenticationContext.User, cancellationToken).ConfigureAwait(false);
return Ok();
}
+
+ [TgsAuthorize]
+ public override async Task GetId(long id, CancellationToken cancellationToken)
+ {
+ var job = await DatabaseContext.Jobs.Where(x => x.Id == id).Include(x => x.StartedBy).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
+ if (job == default(Job))
+ return NotFound();
+ return Json(job.ToApi());
+ }
}
}
diff --git a/src/Tgstation.Server.Host/Controllers/ModelController.cs b/src/Tgstation.Server.Host/Controllers/ModelController.cs
index 59d2b5c19a..54d01677da 100644
--- a/src/Tgstation.Server.Host/Controllers/ModelController.cs
+++ b/src/Tgstation.Server.Host/Controllers/ModelController.cs
@@ -43,6 +43,14 @@ namespace Tgstation.Server.Host.Controllers
[HttpGet]
public virtual Task Read(CancellationToken cancellationToken) => Task.FromResult((IActionResult)NotFound());
+ ///
+ /// Attempt to get a specific a
+ ///
+ /// The for the operation
+ /// A resulting in the of the operation
+ [HttpGet("/{0}")]
+ public virtual Task GetId(long id, CancellationToken cancellationToken) => Task.FromResult((IActionResult)NotFound());
+
///
/// Attempt to update a
///