REST updates.
Rest.cs got a lot of commenting at last. Redirects and upgrades can now be easily added for REST routes via the `Rest.RegisterRedirect(base, target, upgrade)` method. Redirects added for all routes. Upgrades added for `/world/bloodmoon` and `/v2/world/autosave`, as they both use old-style REST verbs.
This commit is contained in:
parent
9d4ced58b9
commit
e3a8112b5b
4 changed files with 346 additions and 31 deletions
|
|
@ -14,9 +14,11 @@ This is the rolling changelog for TShock for Terraria. Use past tense when addin
|
||||||
* Fixed server crashes caused by client disconnections when attempting to read closed sockets (@Enerdy)
|
* Fixed server crashes caused by client disconnections when attempting to read closed sockets (@Enerdy)
|
||||||
* Added some code to make trapdoors work better (@DogooFalchion)
|
* Added some code to make trapdoors work better (@DogooFalchion)
|
||||||
* AllowCutTilesAndBreakables config option now correctly allows flowers/vines/herbs to be cut in regions without breaking walls (@WhiteXZ)
|
* AllowCutTilesAndBreakables config option now correctly allows flowers/vines/herbs to be cut in regions without breaking walls (@WhiteXZ)
|
||||||
* REST: `/status` has been re-added. It will now always point to `/v2/server/status` and includes an `upgrade` field describing the newest status route (@WhiteXZ)
|
|
||||||
* REST: `/v3/players/read` now includes a `muted` field (@WhiteXZ)
|
* REST: `/v3/players/read` now includes a `muted` field (@WhiteXZ)
|
||||||
* REST: Token creation is now more secure (@WhiteXZ) (Thanks to @Plazmaz for reporting the issue!)
|
* REST: Token creation is now more secure (Thanks to @Plazmaz for reporting the issue!)
|
||||||
|
* REST: Deprecated the RestRequestEvent. If you use this event, please let us know.
|
||||||
|
* REST: ALL endpoints now have a base route (eg you can use `/server/motd` instead of `/v3/server/motd`). These base routes will never change, but will provide an `upgrade` field describing any newer routes
|
||||||
|
* REST: Added `/v3/world/autosave` and `/v3/world/bloodmoon` which use GET parameter style arguments. I.e., `/v3/world/autosave?state=[true|false]` & `/v3/world/bloodmoon?state=[true|false]`. The state argument is optional
|
||||||
* Fixed fishing quests not saving/loading correctly when login before join, UUID login, and SSC were enabled together (@DogooFalchion)
|
* Fixed fishing quests not saving/loading correctly when login before join, UUID login, and SSC were enabled together (@DogooFalchion)
|
||||||
|
|
||||||
## TShock 4.3.21
|
## TShock 4.3.21
|
||||||
|
|
|
||||||
|
|
@ -35,18 +35,44 @@ namespace Rests
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Rest command delegate
|
/// Rest command delegate
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="args">RestRequestArgs object containing Verbs, Parameters, Request, and TokenData</param>
|
/// <param name="args"><see cref="RestRequestArgs"/> object containing Verbs, Parameters, Request, and TokenData</param>
|
||||||
/// <returns>Response object or null to not handle request</returns>
|
/// <returns>Response object or null to not handle request</returns>
|
||||||
public delegate object RestCommandD(RestRequestArgs args);
|
public delegate object RestCommandD(RestRequestArgs args);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Describes the data contained in a REST request
|
||||||
|
/// </summary>
|
||||||
public class RestRequestArgs
|
public class RestRequestArgs
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Verbs sent in the request
|
||||||
|
/// </summary>
|
||||||
public RestVerbs Verbs { get; private set; }
|
public RestVerbs Verbs { get; private set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Parameters sent in the request
|
||||||
|
/// </summary>
|
||||||
public IParameterCollection Parameters { get; private set; }
|
public IParameterCollection Parameters { get; private set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The HTTP request
|
||||||
|
/// </summary>
|
||||||
public IRequest Request { get; private set; }
|
public IRequest Request { get; private set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Token data used by the request
|
||||||
|
/// </summary>
|
||||||
public SecureRest.TokenData TokenData { get; private set; }
|
public SecureRest.TokenData TokenData { get; private set; }
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="IHttpContext"/> used by the request
|
||||||
|
/// </summary>
|
||||||
public IHttpContext Context { get; private set; }
|
public IHttpContext Context { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of <see cref="RestRequestArgs"/> with the given verbs, parameters, request, and context.
|
||||||
|
/// No token data is used
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="verbs">Verbs used in the request</param>
|
||||||
|
/// <param name="param">Parameters used in the request</param>
|
||||||
|
/// <param name="request">The HTTP request</param>
|
||||||
|
/// <param name="context">The HTTP context</param>
|
||||||
public RestRequestArgs(RestVerbs verbs, IParameterCollection param, IRequest request, IHttpContext context)
|
public RestRequestArgs(RestVerbs verbs, IParameterCollection param, IRequest request, IHttpContext context)
|
||||||
{
|
{
|
||||||
Verbs = verbs;
|
Verbs = verbs;
|
||||||
|
|
@ -56,6 +82,14 @@ namespace Rests
|
||||||
Context = context;
|
Context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of <see cref="RestRequestArgs"/> with the given verbs, parameters, request, token data, and context.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="verbs">Verbs used in the request</param>
|
||||||
|
/// <param name="param">Parameters used in the request</param>
|
||||||
|
/// <param name="request">The HTTP request</param>
|
||||||
|
/// <param name="tokenData">Token data used in the request</param>
|
||||||
|
/// <param name="context">The HTTP context</param>
|
||||||
public RestRequestArgs(RestVerbs verbs, IParameterCollection param, IRequest request, SecureRest.TokenData tokenData, IHttpContext context)
|
public RestRequestArgs(RestVerbs verbs, IParameterCollection param, IRequest request, SecureRest.TokenData tokenData, IHttpContext context)
|
||||||
{
|
{
|
||||||
Verbs = verbs;
|
Verbs = verbs;
|
||||||
|
|
@ -65,16 +99,39 @@ namespace Rests
|
||||||
Context = context;
|
Context = context;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A RESTful API service
|
||||||
|
/// </summary>
|
||||||
public class Rest : IDisposable
|
public class Rest : IDisposable
|
||||||
{
|
{
|
||||||
private readonly List<RestCommand> commands = new List<RestCommand>();
|
private readonly List<RestCommand> commands = new List<RestCommand>();
|
||||||
|
/// <summary>
|
||||||
|
/// Contains redirect URIs. The key is the base URI. The first item of the tuple is the redirect URI.
|
||||||
|
/// The second item of the tuple is an optional "upgrade" URI which will be added to the REST response.
|
||||||
|
/// </summary>
|
||||||
|
private Dictionary<string, Tuple<string, string>> redirects = new Dictionary<string, Tuple<string, string>>();
|
||||||
private HttpListener listener;
|
private HttpListener listener;
|
||||||
private StringHeader serverHeader;
|
private StringHeader serverHeader;
|
||||||
public Dictionary<string, int> tokenBucket = new Dictionary<string, int>();
|
|
||||||
private Timer tokenBucketTimer;
|
private Timer tokenBucketTimer;
|
||||||
|
/// <summary>
|
||||||
|
/// Contains tokens used to manage REST authentication
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<string, int> tokenBucket = new Dictionary<string, int>();
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="IPAddress"/> the REST service is listening on
|
||||||
|
/// </summary>
|
||||||
public IPAddress Ip { get; set; }
|
public IPAddress Ip { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Port the REST service is listening on
|
||||||
|
/// </summary>
|
||||||
public int Port { get; set; }
|
public int Port { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of <see cref="Rest"/> listening on the given IP and port
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ip"><see cref="IPAddress"/> to listen on</param>
|
||||||
|
/// <param name="port">Port to listen on</param>
|
||||||
public Rest(IPAddress ip, int port)
|
public Rest(IPAddress ip, int port)
|
||||||
{
|
{
|
||||||
Ip = ip;
|
Ip = ip;
|
||||||
|
|
@ -83,6 +140,9 @@ namespace Rests
|
||||||
serverHeader = new StringHeader("Server", String.Format("{0}/{1}", assembly.Name, assembly.Version));
|
serverHeader = new StringHeader("Server", String.Format("{0}/{1}", assembly.Name, assembly.Version));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Starts the RESTful API service
|
||||||
|
/// </summary>
|
||||||
public virtual void Start()
|
public virtual void Start()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
@ -106,6 +166,11 @@ namespace Rests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Starts the RESTful API service using the given <see cref="IPAddress"/> and port
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ip"><see cref="IPAddress"/> to listen on</param>
|
||||||
|
/// <param name="port">Port to listen on</param>
|
||||||
public void Start(IPAddress ip, int port)
|
public void Start(IPAddress ip, int port)
|
||||||
{
|
{
|
||||||
Ip = ip;
|
Ip = ip;
|
||||||
|
|
@ -113,21 +178,56 @@ namespace Rests
|
||||||
Start();
|
Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stops the RESTful API service
|
||||||
|
/// </summary>
|
||||||
public virtual void Stop()
|
public virtual void Stop()
|
||||||
{
|
{
|
||||||
listener.Stop();
|
listener.Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers a command using the given route
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">URL route</param>
|
||||||
|
/// <param name="callback">Command callback</param>
|
||||||
public void Register(string path, RestCommandD callback)
|
public void Register(string path, RestCommandD callback)
|
||||||
{
|
{
|
||||||
AddCommand(new RestCommand(path, callback));
|
AddCommand(new RestCommand(path, callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers a <see cref="RestCommand"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="com"><see cref="RestCommand"/> to register</param>
|
||||||
public void Register(RestCommand com)
|
public void Register(RestCommand com)
|
||||||
{
|
{
|
||||||
AddCommand(com);
|
AddCommand(com);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers a redirection from a given REST route to a target REST route, with an optional upgrade URI
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="baseRoute">The base URI that will be requested</param>
|
||||||
|
/// <param name="targetRoute">The target URI to redirect to from the base URI</param>
|
||||||
|
/// <param name="upgradeRoute">The upgrade route that will be added as an object to the <see cref="RestObject"/> response of the target route</param>
|
||||||
|
/// <param name="parameterized">Whether the route uses parameterized querying or not.</param>
|
||||||
|
public void RegisterRedirect(string baseRoute, string targetRoute, string upgradeRoute = null, bool parameterized = true)
|
||||||
|
{
|
||||||
|
if (redirects.ContainsKey(baseRoute))
|
||||||
|
{
|
||||||
|
redirects.Add(baseRoute, Tuple.Create(targetRoute, upgradeRoute));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
redirects[baseRoute] = Tuple.Create(targetRoute, upgradeRoute);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a <see cref="RestCommand"/> to the service's command list
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="com"><see cref="RestCommand"/> to add</param>
|
||||||
protected void AddCommand(RestCommand com)
|
protected void AddCommand(RestCommand com)
|
||||||
{
|
{
|
||||||
commands.Add(com);
|
commands.Add(com);
|
||||||
|
|
@ -151,11 +251,13 @@ namespace Rests
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Event
|
#region Event
|
||||||
|
[Obsolete("This class will be removed in the next release")]
|
||||||
public class RestRequestEventArgs : HandledEventArgs
|
public class RestRequestEventArgs : HandledEventArgs
|
||||||
{
|
{
|
||||||
public RequestEventArgs Request { get; set; }
|
public RequestEventArgs Request { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Obsolete("This method will be removed in the next release")]
|
||||||
public static HandlerList<RestRequestEventArgs> RestRequestEvent;
|
public static HandlerList<RestRequestEventArgs> RestRequestEvent;
|
||||||
|
|
||||||
private static bool OnRestRequestCall(RequestEventArgs request)
|
private static bool OnRestRequestCall(RequestEventArgs request)
|
||||||
|
|
@ -172,7 +274,11 @@ namespace Rests
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when the <see cref="HttpListener"/> receives a request
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender">Sender of the request</param>
|
||||||
|
/// <param name="e">RequestEventArgs received</param>
|
||||||
protected virtual void OnRequest(object sender, RequestEventArgs e)
|
protected virtual void OnRequest(object sender, RequestEventArgs e)
|
||||||
{
|
{
|
||||||
var obj = ProcessRequest(sender, e);
|
var obj = ProcessRequest(sender, e);
|
||||||
|
|
@ -196,12 +302,25 @@ namespace Rests
|
||||||
e.Response.Status = HttpStatusCode.OK;
|
e.Response.Status = HttpStatusCode.OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to process a request received by the <see cref="HttpListener"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender">Sender of the request</param>
|
||||||
|
/// <param name="e">RequestEventArgs received</param>
|
||||||
|
/// <returns>A <see cref="RestObject"/> describing the state of the request</returns>
|
||||||
protected virtual object ProcessRequest(object sender, RequestEventArgs e)
|
protected virtual object ProcessRequest(object sender, RequestEventArgs e)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var uri = e.Request.Uri.AbsolutePath;
|
var uri = e.Request.Uri.AbsolutePath;
|
||||||
uri = uri.TrimEnd('/');
|
uri = uri.TrimEnd('/');
|
||||||
|
string upgrade = null;
|
||||||
|
|
||||||
|
if (redirects.ContainsKey(uri))
|
||||||
|
{
|
||||||
|
upgrade = redirects[uri].Item2;
|
||||||
|
uri = redirects[uri].Item1;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var com in commands)
|
foreach (var com in commands)
|
||||||
{
|
{
|
||||||
|
|
@ -224,7 +343,17 @@ namespace Rests
|
||||||
|
|
||||||
var obj = ExecuteCommand(com, verbs, e.Request.Parameters, e.Request, e.Context);
|
var obj = ExecuteCommand(com, verbs, e.Request.Parameters, e.Request, e.Context);
|
||||||
if (obj != null)
|
if (obj != null)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(upgrade) && obj is RestObject)
|
||||||
|
{
|
||||||
|
if (!(obj as RestObject).ContainsKey("upgrade"))
|
||||||
|
{
|
||||||
|
(obj as RestObject).Add("upgrade", upgrade);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
|
|
@ -242,6 +371,15 @@ namespace Rests
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes a <see cref="RestCommand"/> using the provided verbs, parameters, request, and context objects
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cmd">The REST command to execute</param>
|
||||||
|
/// <param name="verbs">The REST verbs used in the command</param>
|
||||||
|
/// <param name="parms">The REST parameters used in the command</param>
|
||||||
|
/// <param name="request">The HTTP request object associated with the command</param>
|
||||||
|
/// <param name="context">The HTTP context associated with the command</param>
|
||||||
|
/// <returns></returns>
|
||||||
protected virtual object ExecuteCommand(RestCommand cmd, RestVerbs verbs, IParameterCollection parms, IRequest request, IHttpContext context)
|
protected virtual object ExecuteCommand(RestCommand cmd, RestVerbs verbs, IParameterCollection parms, IRequest request, IHttpContext context)
|
||||||
{
|
{
|
||||||
object result = cmd.Execute(verbs, parms, request, context);
|
object result = cmd.Execute(verbs, parms, request, context);
|
||||||
|
|
@ -253,6 +391,14 @@ namespace Rests
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds a request URI from the parameters, verbs, and URI template of a <see cref="RestCommand"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cmd">The REST command to take the URI template from</param>
|
||||||
|
/// <param name="verbs">Verbs used in building the URI string</param>
|
||||||
|
/// <param name="parms">Parameters used in building the URI string</param>
|
||||||
|
/// <param name="includeToken">Whether or not to include a token in the URI</param>
|
||||||
|
/// <returns></returns>
|
||||||
protected virtual string BuildRequestUri(
|
protected virtual string BuildRequestUri(
|
||||||
RestCommand cmd, RestVerbs verbs, IParameterCollection parms, bool includeToken = true
|
RestCommand cmd, RestVerbs verbs, IParameterCollection parms, bool includeToken = true
|
||||||
) {
|
) {
|
||||||
|
|
@ -276,12 +422,19 @@ namespace Rests
|
||||||
|
|
||||||
#region Dispose
|
#region Dispose
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disposes the RESTful API service
|
||||||
|
/// </summary>
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Dispose(true);
|
Dispose(true);
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disposes the RESTful API service
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing"></param>
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (disposing)
|
if (disposing)
|
||||||
|
|
@ -294,6 +447,9 @@ namespace Rests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Destructor
|
||||||
|
/// </summary>
|
||||||
~Rest()
|
~Rest()
|
||||||
{
|
{
|
||||||
Dispose(false);
|
Dispose(false);
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ namespace Rests
|
||||||
private RestCommandD callback;
|
private RestCommandD callback;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
/// Creates a new <see cref="RestCommand"/> used with the REST API
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="name">Used for identification</param>
|
/// <param name="name">Used for identification</param>
|
||||||
/// <param name="uritemplate">Url template</param>
|
/// <param name="uritemplate">Url template</param>
|
||||||
|
|
@ -51,7 +51,7 @@ namespace Rests
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
/// Creates a new <see cref="RestCommand"/> used with the REST API
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="uritemplate">Url template</param>
|
/// <param name="uritemplate">Url template</param>
|
||||||
/// <param name="callback">Rest Command callback</param>
|
/// <param name="callback">Rest Command callback</param>
|
||||||
|
|
|
||||||
|
|
@ -31,35 +31,78 @@ using TShockAPI.DB;
|
||||||
|
|
||||||
namespace TShockAPI
|
namespace TShockAPI
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Describes the permission required to use an API route
|
||||||
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
|
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
|
||||||
public class Permission : Attribute
|
public class Permission : Attribute
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Name of the permission
|
||||||
|
/// </summary>
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of <see cref="Permission"/> with the given name
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Permission required</param>
|
||||||
public Permission(string name)
|
public Permission(string name)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Describes the route of a REST API call
|
||||||
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Method)]
|
[AttributeUsage(AttributeTargets.Method)]
|
||||||
public class RouteAttribute : Attribute
|
public class RouteAttribute : Attribute
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The route used to call the API
|
||||||
|
/// </summary>
|
||||||
public string Route { get; set; }
|
public string Route { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of <see cref="RouteAttribute"/> with the given route
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="route">Route used to call the API</param>
|
||||||
public RouteAttribute(string route)
|
public RouteAttribute(string route)
|
||||||
{
|
{
|
||||||
Route = route;
|
Route = route;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Describes a parameter in a REST route
|
||||||
|
/// </summary>
|
||||||
public class ParameterAttribute : Attribute
|
public class ParameterAttribute : Attribute
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The parameter's name
|
||||||
|
/// </summary>
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the parameter is required or not
|
||||||
|
/// </summary>
|
||||||
public bool Required { get; set; }
|
public bool Required { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The parameter's description
|
||||||
|
/// </summary>
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The parameter's System Type
|
||||||
|
/// </summary>
|
||||||
public Type ArgumentType { get; set; }
|
public Type ArgumentType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of <see cref="ParameterAttribute"/> with the given name, description, and type.
|
||||||
|
/// A ParameterAttribute may be optional or required.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name"></param>
|
||||||
|
/// <param name="req"></param>
|
||||||
|
/// <param name="desc"></param>
|
||||||
|
/// <param name="type"></param>
|
||||||
public ParameterAttribute(string name, bool req, string desc, Type type)
|
public ParameterAttribute(string name, bool req, string desc, Type type)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
|
|
@ -69,51 +112,136 @@ namespace TShockAPI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Describes a parameter in a REST route
|
||||||
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
|
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
|
||||||
public class Noun : ParameterAttribute
|
public class Noun : ParameterAttribute
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of <see cref="Noun"/> with the given name, description, and type.
|
||||||
|
/// Nouns may be optional or required. A required Noun is akin to a <see cref="Verb"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Name of the noun</param>
|
||||||
|
/// <param name="req">Whether the noun is required or not</param>
|
||||||
|
/// <param name="desc">Decription of the noun</param>
|
||||||
|
/// <param name="type">System Type of the noun</param>
|
||||||
public Noun(string name, bool req, string desc, Type type) : base(name, req, desc, type) { }
|
public Noun(string name, bool req, string desc, Type type) : base(name, req, desc, type) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Describes a parameter in a REST route
|
||||||
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
|
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
|
||||||
public class Verb : ParameterAttribute
|
public class Verb : ParameterAttribute
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of <see cref="Verb"/> with the given name, description, and type.
|
||||||
|
/// Verbs are required arguments.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Name of the verb</param>
|
||||||
|
/// <param name="desc">Description of the verb</param>
|
||||||
|
/// <param name="type">System Type of the verb</param>
|
||||||
public Verb(string name, string desc, Type type) : base(name, true, desc, type) { }
|
public Verb(string name, string desc, Type type) : base(name, true, desc, type) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Describes a REST authentication token
|
||||||
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Method)]
|
[AttributeUsage(AttributeTargets.Method)]
|
||||||
public class Token : Noun
|
public class Token : Noun
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of <see cref="Token"/>
|
||||||
|
/// </summary>
|
||||||
public Token() : base("token", true, "The REST authentication token.", typeof(String)){}
|
public Token() : base("token", true, "The REST authentication token.", typeof(String)){}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Manages a <see cref="Rests.Rest"/> instance
|
||||||
|
/// </summary>
|
||||||
public class RestManager
|
public class RestManager
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The RESTful API service that handles API requests
|
||||||
|
/// </summary>
|
||||||
private Rest Rest;
|
private Rest Rest;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of <see cref="RestManager"/> using the provided <see cref="Rest"/> object
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="rest"></param>
|
||||||
public RestManager(Rest rest)
|
public RestManager(Rest rest)
|
||||||
{
|
{
|
||||||
Rest = rest;
|
Rest = rest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers default TShock REST commands
|
||||||
|
/// </summary>
|
||||||
public void RegisterRestfulCommands()
|
public void RegisterRestfulCommands()
|
||||||
{
|
{
|
||||||
// Server Commands
|
// Server Commands
|
||||||
if (TShock.Config.EnableTokenEndpointAuthentication)
|
if (TShock.Config.EnableTokenEndpointAuthentication)
|
||||||
{
|
{
|
||||||
Rest.Register(new SecureRestCommand("/status", ServerStatusRoot));
|
|
||||||
Rest.Register(new SecureRestCommand("/v2/server/status", ServerStatusV2));
|
Rest.Register(new SecureRestCommand("/v2/server/status", ServerStatusV2));
|
||||||
Rest.Register(new SecureRestCommand("/v3/server/motd", ServerMotd));
|
Rest.Register(new SecureRestCommand("/v3/server/motd", ServerMotd));
|
||||||
Rest.Register(new SecureRestCommand("/v3/server/rules", ServerRules));
|
Rest.Register(new SecureRestCommand("/v3/server/rules", ServerRules));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Rest.Register(new RestCommand("/status", (a) => this.ServerStatusRoot(new RestRequestArgs(a.Verbs, a.Parameters, a.Request, SecureRest.TokenData.None, a.Context))));
|
Rest.Register(new RestCommand("/v2/server/status", (a) => ServerStatusV2(new RestRequestArgs(a.Verbs, a.Parameters, a.Request, SecureRest.TokenData.None, a.Context))));
|
||||||
Rest.Register(new RestCommand("/v2/server/status", (a) => this.ServerStatusV2(new RestRequestArgs(a.Verbs, a.Parameters, a.Request, SecureRest.TokenData.None, a.Context))));
|
Rest.Register(new RestCommand("/v3/server/motd", (a) => ServerMotd(new RestRequestArgs(a.Verbs, a.Parameters, a.Request, SecureRest.TokenData.None, a.Context))));
|
||||||
Rest.Register(new RestCommand("/v3/server/motd", (a) => this.ServerMotd(new RestRequestArgs(a.Verbs, a.Parameters, a.Request, SecureRest.TokenData.None, a.Context))));
|
Rest.Register(new RestCommand("/v3/server/rules", (a) => ServerRules(new RestRequestArgs(a.Verbs, a.Parameters, a.Request, SecureRest.TokenData.None, a.Context))));
|
||||||
Rest.Register(new RestCommand("/v3/server/rules", (a) => this.ServerRules(new RestRequestArgs(a.Verbs, a.Parameters, a.Request, SecureRest.TokenData.None, a.Context))));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rest.RegisterRedirect("/status", "/v2/server/status");
|
||||||
|
|
||||||
|
//server commands
|
||||||
|
Rest.RegisterRedirect("/server/motd", "/v3/server/motd");
|
||||||
|
Rest.RegisterRedirect("/server/rules", "/v3/server/rules");
|
||||||
|
Rest.RegisterRedirect("/server/broadcast", "/v2/server/broadcast");
|
||||||
|
Rest.RegisterRedirect("/server/reload", "/v2/server/reload");
|
||||||
|
Rest.RegisterRedirect("/server/off", "/v2/server/off");
|
||||||
|
Rest.RegisterRedirect("/server/restart", "/v3/server/restart");
|
||||||
|
Rest.RegisterRedirect("/server/rawcmd", "/v3/server/rawcmd");
|
||||||
|
|
||||||
|
//user commands
|
||||||
|
Rest.RegisterRedirect("/users/activelist", "/v2/users/activelist");
|
||||||
|
Rest.RegisterRedirect("/users/create", "/v2/users/create");
|
||||||
|
Rest.RegisterRedirect("/users/list", "/v2/users/list");
|
||||||
|
Rest.RegisterRedirect("/users/read", "/v2/users/read");
|
||||||
|
Rest.RegisterRedirect("/users/destroy", "/v2/users/destroy");
|
||||||
|
Rest.RegisterRedirect("/users/update", "/v2/users/update");
|
||||||
|
|
||||||
|
//ban commands
|
||||||
|
Rest.RegisterRedirect("/bans/list", "/v2/bans/list");
|
||||||
|
Rest.RegisterRedirect("/bans/read", "/v2/bans/read");
|
||||||
|
Rest.RegisterRedirect("/bans/destroy", "/v2/bans/destroy");
|
||||||
|
|
||||||
|
//world commands
|
||||||
|
Rest.RegisterRedirect("/world/bloodmoon", "v3/world/bloodmoon");
|
||||||
|
Rest.RegisterRedirect("/world/save", "/v2/world/save");
|
||||||
|
Rest.RegisterRedirect("/world/autosave", "/v3/world/autosave");
|
||||||
|
|
||||||
|
//player commands
|
||||||
|
Rest.RegisterRedirect("/lists/players", "/lists/players", "/v2/players/list");
|
||||||
|
Rest.RegisterRedirect("/players/list", "/v2/players/list");
|
||||||
|
Rest.RegisterRedirect("/players/read", "/v3/players/read");
|
||||||
|
Rest.RegisterRedirect("/players/kick", "/v2/players/kick");
|
||||||
|
Rest.RegisterRedirect("/players/ban", "/v2/players/ban");
|
||||||
|
Rest.RegisterRedirect("/players/kill", "/v2/players/kill");
|
||||||
|
Rest.RegisterRedirect("/players/mute", "/v2/players/mute");
|
||||||
|
Rest.RegisterRedirect("/players/unmute", "/v2/players/unmute");
|
||||||
|
|
||||||
|
//group commands
|
||||||
|
Rest.RegisterRedirect("/groups/list", "/v2/groups/list");
|
||||||
|
Rest.RegisterRedirect("/groups/read", "/v2/groups/read");
|
||||||
|
Rest.RegisterRedirect("/groups/destroy", "/v2/groups/destroy");
|
||||||
|
Rest.RegisterRedirect("/groups/create", "/v2/groups/create");
|
||||||
|
Rest.RegisterRedirect("/groups/update", "/v2/groups/update");
|
||||||
|
|
||||||
|
|
||||||
Rest.Register(new SecureRestCommand("/v2/server/broadcast", ServerBroadcast));
|
Rest.Register(new SecureRestCommand("/v2/server/broadcast", ServerBroadcast));
|
||||||
Rest.Register(new SecureRestCommand("/v3/server/reload", ServerReload, RestPermissions.restcfg));
|
Rest.Register(new SecureRestCommand("/v3/server/reload", ServerReload, RestPermissions.restcfg));
|
||||||
Rest.Register(new SecureRestCommand("/v2/server/off", ServerOff, RestPermissions.restmaintenance));
|
Rest.Register(new SecureRestCommand("/v2/server/off", ServerOff, RestPermissions.restmaintenance));
|
||||||
|
|
@ -139,8 +267,10 @@ namespace TShockAPI
|
||||||
Rest.Register(new SecureRestCommand("/world/read", WorldRead));
|
Rest.Register(new SecureRestCommand("/world/read", WorldRead));
|
||||||
Rest.Register(new SecureRestCommand("/world/meteor", WorldMeteor, RestPermissions.restcauseevents));
|
Rest.Register(new SecureRestCommand("/world/meteor", WorldMeteor, RestPermissions.restcauseevents));
|
||||||
Rest.Register(new SecureRestCommand("/world/bloodmoon/{bloodmoon}", WorldBloodmoon, RestPermissions.restcauseevents));
|
Rest.Register(new SecureRestCommand("/world/bloodmoon/{bloodmoon}", WorldBloodmoon, RestPermissions.restcauseevents));
|
||||||
|
Rest.Register(new SecureRestCommand("/v3/world/bloomoon", WorldBloodmoonV3, RestPermissions.restcauseevents));
|
||||||
Rest.Register(new SecureRestCommand("/v2/world/save", WorldSave, RestPermissions.restcfg));
|
Rest.Register(new SecureRestCommand("/v2/world/save", WorldSave, RestPermissions.restcfg));
|
||||||
Rest.Register(new SecureRestCommand("/v2/world/autosave/state/{state}", WorldChangeSaveSettings, RestPermissions.restcfg));
|
Rest.Register(new SecureRestCommand("/v2/world/autosave/state/{state}", WorldChangeSaveSettings, RestPermissions.restcfg));
|
||||||
|
Rest.Register(new SecureRestCommand("/v3/world/autosave", WorldChangeSaveSettingsV3, RestPermissions.restcfg));
|
||||||
Rest.Register(new SecureRestCommand("/v2/world/butcher", WorldButcher, RestPermissions.restbutcher));
|
Rest.Register(new SecureRestCommand("/v2/world/butcher", WorldButcher, RestPermissions.restbutcher));
|
||||||
|
|
||||||
// Player Commands
|
// Player Commands
|
||||||
|
|
@ -161,7 +291,7 @@ namespace TShockAPI
|
||||||
Rest.Register(new SecureRestCommand("/v2/groups/update", GroupUpdate, RestPermissions.restmanagegroups));
|
Rest.Register(new SecureRestCommand("/v2/groups/update", GroupUpdate, RestPermissions.restmanagegroups));
|
||||||
}
|
}
|
||||||
|
|
||||||
#region RestServerMethods
|
#region Rest Server Methods
|
||||||
|
|
||||||
[Description("Executes a remote command on the server, and returns the output of the command.")]
|
[Description("Executes a remote command on the server, and returns the output of the command.")]
|
||||||
[RouteAttribute("/v3/server/rawcmd")]
|
[RouteAttribute("/v3/server/rawcmd")]
|
||||||
|
|
@ -275,16 +405,6 @@ namespace TShockAPI
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[Description("Get a list of information about the current TShock server.")]
|
|
||||||
[Route("/status")]
|
|
||||||
[Token]
|
|
||||||
private object ServerStatusRoot(RestRequestArgs args)
|
|
||||||
{
|
|
||||||
RestObject status = (RestObject)ServerStatusV2(args);
|
|
||||||
status.Add("upgrade", "/v2/server/status");
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Description("Get a list of information about the current TShock server.")]
|
[Description("Get a list of information about the current TShock server.")]
|
||||||
[Route("/v2/server/status")]
|
[Route("/v2/server/status")]
|
||||||
[Token]
|
[Token]
|
||||||
|
|
@ -351,7 +471,7 @@ namespace TShockAPI
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region RestUserMethods
|
#region Rest User Methods
|
||||||
|
|
||||||
[Description("Returns the list of user accounts that are currently in use on the server.")]
|
[Description("Returns the list of user accounts that are currently in use on the server.")]
|
||||||
[Route("/v2/users/activelist")]
|
[Route("/v2/users/activelist")]
|
||||||
|
|
@ -502,7 +622,7 @@ namespace TShockAPI
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region RestBanMethods
|
#region Rest Ban Methods
|
||||||
|
|
||||||
[Description("Create a new ban entry.")]
|
[Description("Create a new ban entry.")]
|
||||||
[Route("/bans/create")]
|
[Route("/bans/create")]
|
||||||
|
|
@ -614,8 +734,8 @@ namespace TShockAPI
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region RestWorldMethods
|
#region Rest World Methods
|
||||||
|
|
||||||
[Route("/v2/world/autosave/state/{state}")]
|
[Route("/v2/world/autosave/state/{state}")]
|
||||||
[Permission(RestPermissions.restcfg)]
|
[Permission(RestPermissions.restcfg)]
|
||||||
[Verb("state", "The status for autosave.", typeof(bool))]
|
[Verb("state", "The status for autosave.", typeof(bool))]
|
||||||
|
|
@ -627,7 +747,25 @@ namespace TShockAPI
|
||||||
return RestInvalidParam("state");
|
return RestInvalidParam("state");
|
||||||
TShock.Config.AutoSave = autoSave;
|
TShock.Config.AutoSave = autoSave;
|
||||||
|
|
||||||
return RestResponse("AutoSave has been set to " + autoSave);
|
var resp = RestResponse("AutoSave has been set to " + autoSave);
|
||||||
|
resp.Add("upgrade", "/v3/world/autosave");
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Route("/v3/world/autosave")]
|
||||||
|
[Permission(RestPermissions.restcfg)]
|
||||||
|
[Parameter("state", false, "The status for autosave.", typeof(bool))]
|
||||||
|
[Token]
|
||||||
|
private object WorldChangeSaveSettingsV3(RestRequestArgs args)
|
||||||
|
{
|
||||||
|
bool autoSave;
|
||||||
|
if (!bool.TryParse(args.Parameters["state"], out autoSave))
|
||||||
|
{
|
||||||
|
return RestResponse($"Autosave is currently {(TShock.Config.AutoSave ? "enabled" : "disabled")}");
|
||||||
|
}
|
||||||
|
TShock.Config.AutoSave = autoSave;
|
||||||
|
|
||||||
|
return RestResponse($"AutoSave has been {(TShock.Config.AutoSave ? "enabled" : "disabled")}");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Description("Save the world.")]
|
[Description("Save the world.")]
|
||||||
|
|
@ -704,12 +842,31 @@ namespace TShockAPI
|
||||||
return RestInvalidParam("bloodmoon");
|
return RestInvalidParam("bloodmoon");
|
||||||
Main.bloodMoon = bloodmoon;
|
Main.bloodMoon = bloodmoon;
|
||||||
|
|
||||||
return RestResponse("Blood Moon has been set to " + bloodmoon);
|
var resp = RestResponse("Blood Moon has been set to " + bloodmoon);
|
||||||
|
resp.Add("upgrade", "/v3/world/bloodmoon");
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Description("Toggle the status of blood moon.")]
|
||||||
|
[Route("/v3/world/bloodmoon")]
|
||||||
|
[Permission(RestPermissions.restcauseevents)]
|
||||||
|
[Parameter("state", false, "Sets the state of the bloodmoon.", typeof(bool))]
|
||||||
|
[Token]
|
||||||
|
private object WorldBloodmoonV3(RestRequestArgs args)
|
||||||
|
{
|
||||||
|
bool bloodmoon;
|
||||||
|
if (!bool.TryParse(args.Verbs["state"], out bloodmoon))
|
||||||
|
{
|
||||||
|
return RestResponse($"Bloodmoon state: {(Main.bloodMoon ? "Enabled" : "Disabled")}");
|
||||||
|
}
|
||||||
|
Main.bloodMoon = bloodmoon;
|
||||||
|
|
||||||
|
return RestResponse($"Blood Moon has been {(Main.bloodMoon ? "enabled" : "disabled")}");
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region RestPlayerMethods
|
#region Rest Player Methods
|
||||||
|
|
||||||
[Description("Unmute a player.")]
|
[Description("Unmute a player.")]
|
||||||
[Route("/v2/players/unmute")]
|
[Route("/v2/players/unmute")]
|
||||||
|
|
@ -844,7 +1001,7 @@ namespace TShockAPI
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region RestGroupMethods
|
#region Rest Group Methods
|
||||||
|
|
||||||
[Description("View all groups in the TShock database.")]
|
[Description("View all groups in the TShock database.")]
|
||||||
[Route("/v2/groups/list")]
|
[Route("/v2/groups/list")]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue