diff --git a/docs/API.dox b/docs/API.dox new file mode 100644 index 0000000000..683fa11d29 --- /dev/null +++ b/docs/API.dox @@ -0,0 +1,106 @@ +@page api API + +@tableofcontents + +@section api_intro Introduction + +The TGS4 API is designed to be a fully realized RESTful service. Once hosted, follow the specified protocol for developing new clients or one off requests that provide full control over the server + +Routes and their usages are defined as follows + +[I (If Instance is required)] "" => + +@section api_lib Official Libraries + +The TGS4 API's canonical definitions are provided as a .NET Standard library in the form of a nuget package located here: https://www.nuget.org/packages/Tgstation.Server.Api + +An all inclusive TAP interface for using the API is also provided in this package: https://www.nuget.org/packages/Tgstation.Server.Client + +@subsection api_interp Interpreting C# Models + +This document will reference the canonical C# models in the Tgstation.Server.Api namespace. Note that these models are built to mirror the JSON requests and responses with a couple caveats. + +- The first letter of every field name will/must be lowercase in JSON models +- Fields in the C# model marked with "DenyWrite = true" cannot be changed using POST requests +- Fields marked 'Required' should be ignored, this is a semantic for the backing SQL database +- Id fields must be specified when making POST requests +- All other fields are optional and may be absent from responses or requests unless otherwise specified + +@section api_header Headers + +TGS4 expects this set of headers. Failure to provide them may result in + +- User-Agent: The user agent product header value of the calling program. Should be in the form Agent/Version (i.e. SomeTgsClient/1.2.4) +- Accept: application/json +- Api: Another product header value representing the version of the API to use. Currently this must be: Tgstation.Server.Api/4.0.0.0 + +For POST and PUT requests you must also include the content type. Currently only json is supported + +- Content-Type: application/json + +For requests made to any Instance based API's a header with the Instance's ID must be provided + +- Instance: The ID of the instance being accessed + +An Authentication header is also required. See @ref api_auth + +@section api_response Response Codes + +TGS will only every return the response codes listed here + +- 200: General OK status. Unless the HTTP DELETE verb was used to make the request (In which case the response body will be empty), the response body will contain a json model or array depending on the API called. +- 400: Bad request made. The response body will contain a @ref Tgstation.Server.Api.Models.ErrorMessage model detailing the error +- 401: User unauthorized. Invalid or expired credentials were provided. Check rights APIs for updates. See @ref api_auth for details +- 403: Usage forbidden. User tried to make a request they were not allowed to perform. +- 404: Not found. A resource was requested that had never existed. In the case of retrieving a resource by ID, it could potentially exist in the future +- 406: Not acceptable. Consequence of failing to provide an Accept header +- 408: Request timeout. The client took to long to continue a request +- 409: Conflict. Documented in the requests that use them +- 410: Gone. Attempted to access/modify a resource that ideally should have been ready, but isn't or no longer is +- 419: Rate limited. Used with operations that rely on GitHub.com. If a rate limit is hit for an operation this will be returned +- 500: Server error. Please report the request and response body to the code repository +- 501: Not implemented. Currently used in two places: 1. Endpoints that trigger a server restart but the server is not running in a restartable configuration. 2. +- 503: Service unavailable. The server is either starting up or shutting down and isn't ready to respond to requests. You can try again soon and a response/lack thereof will indicate which of the two events it was + +@section api_auth Authentication + +Every request made to TGS requires authentication. It is provided in the form of the Authorization header. + +The first request made to TGS must be to login the user + +POST "/" => Empty + +Headers: + +- Username: +- Authorization:Password + +If the provided credentials are valid and your user account is enabled you will recieve a @ref Tgstation.Server.Api.Models.Token object +@code{.json} +{ + "bearer": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwiZXhwIjoiMTUzMzIzNjk0MSIsIm5iZiI6IjE1MzMyMzYwNDEiLCJpc3MiOiJUZ3N0YXRpb24uU2VydmVyLkhvc3QiLCJhdWQiOiJUZ3N0YXRpb24uU2VydmVyLkFwaSJ9.QWFSWNi9mgol582zaK4MQ5XDK2gZF-Nx3z9_ToHjKW4" +} +@endcode + +If your account is disabled, you will recieve a 403 response. + +You may recognize the bearer value as a Json Web Token. This is a secure representation of your identity to the server. It expires after a set period of time or until your password changes. It must be present for requests made to all other APIs. To do so add the following header to your other requests + +- Authorization:Bearer + +Continue to use this token until you begin to recieve 401 responses from the API. Then repeat the process to get a new one if your credentials are still valid + +@section api_perms Permissions + +Almost all actions available require some level of user permissions. Which are divided into two categories: + +- General permissions apply to a user across the entire service +- Instance permissions apply only when interacting with a specific instance + +Permissions are represented a a 32 bit integer in the form of bitflags. The two endpoints for retrieving these permission are: + +GET "/Users" => @ref Tgstation.Server.Api.Models.User +I GET "/InstanceUser" => @ref Tgstation.Server.Api.Models.InstanceUser + +See individual permission documentation for their usage + diff --git a/src/Tgstation.Server.Api/ApiHeaders.cs b/src/Tgstation.Server.Api/ApiHeaders.cs index 597bad25cc..477ba75149 100644 --- a/src/Tgstation.Server.Api/ApiHeaders.cs +++ b/src/Tgstation.Server.Api/ApiHeaders.cs @@ -20,6 +20,11 @@ namespace Tgstation.Server.Api /// public const string ApplicationJson = "application/json"; + /// + /// The header key + /// + const string ApiVersionHeader = "Api"; + /// /// The header key /// @@ -28,7 +33,7 @@ namespace Tgstation.Server.Api /// /// The header key /// - const string instanceIdHeader = "InstanceId"; + const string instanceIdHeader = "Instance"; /// /// The JWT authentication header scheme @@ -125,16 +130,16 @@ namespace Tgstation.Server.Api //assure the client user agent has a name and version if (String.IsNullOrWhiteSpace(clientUserAgent.Product.Name) || !Version.TryParse(clientUserAgent.Product.Version, out var clientVersion)) throw new InvalidOperationException("Malformed client user agent!"); - - var apiUserAgentHeader = userAgentValues[1]; + //make sure the api header matches ours - if (!ProductInfoHeaderValue.TryParse(apiUserAgentHeader, out var apiUserAgent) || apiUserAgent.Product.Name != assemblyName.Name) + if (!requestHeaders.Headers.TryGetValue(ApiVersionHeader, out var apiUserAgentHeaderValues) || !ProductInfoHeaderValue.TryParse(apiUserAgentHeaderValues.FirstOrDefault(), out var apiUserAgent) || apiUserAgent.Product.Name != assemblyName.Name) throw new InvalidOperationException("Missing API user agent!"); if (!Version.TryParse(apiUserAgent.Product.Version, out var apiVersion)) throw new InvalidOperationException("Malformed API version!"); ApiVersion = apiVersion; + UserAgent = clientUserAgent.Product; //check api version compatibility var ourVersion = assemblyName.Version; @@ -217,7 +222,7 @@ namespace Tgstation.Server.Api headers.Add(usernameHeader, Username); } headers.UserAgent.Add(new ProductInfoHeaderValue(UserAgent)); - headers.UserAgent.Add(new ProductInfoHeaderValue(new ProductHeaderValue(assemblyName.Name, ApiVersion.ToString()))); + headers.Add(ApiVersionHeader, ApiVersion.ToString()); if(InstanceId.HasValue) headers.Add(instanceIdHeader, InstanceId.ToString()); } diff --git a/src/Tgstation.Server.Api/Models/ErrorMessage.cs b/src/Tgstation.Server.Api/Models/ErrorMessage.cs new file mode 100644 index 0000000000..1be77b7090 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/ErrorMessage.cs @@ -0,0 +1,13 @@ +namespace Tgstation.Server.Api.Models +{ + /// + /// Represents an error message returned by the server + /// + public sealed class ErrorMessage + { + /// + /// A human readable description of the error + /// + public string Message { get; set; } + } +} diff --git a/src/Tgstation.Server.Host/Controllers/ApiController.cs b/src/Tgstation.Server.Host/Controllers/ApiController.cs index 8767a735b9..a2e06dc537 100644 --- a/src/Tgstation.Server.Host/Controllers/ApiController.cs +++ b/src/Tgstation.Server.Host/Controllers/ApiController.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; using System.Globalization; using System.IdentityModel.Tokens.Jwt; +using System.Linq; using System.Security.Claims; using System.Threading.Tasks; using Tgstation.Server.Api; @@ -166,6 +167,13 @@ namespace Tgstation.Server.Host.Controllers return; } + if(ModelState?.IsValid == false) + { + var errorMessages = ModelState.SelectMany(x => x.Value.Errors).Select(x => x.ErrorMessage); + await BadRequest(new { message = String.Join(Environment.NewLine, errorMessages) }).ExecuteResultAsync(context).ConfigureAwait(false); + return; + } + Logger.LogInformation("Request made by User ID {0}. Api version: {1}. User-Agent: {2}", AuthenticationContext?.User.Id.ToString(CultureInfo.InvariantCulture) ?? "NULL", ApiHeaders.ApiVersion, ApiHeaders.UserAgent); await base.OnActionExecutionAsync(context, next).ConfigureAwait(false); } diff --git a/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs b/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs index 04b226a30a..1173ad68e6 100644 --- a/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs +++ b/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs @@ -82,7 +82,7 @@ namespace Tgstation.Server.Host.Controllers { var result = await instanceManager.GetInstance(Instance).Configuration.Read(path, AuthenticationContext.SystemIdentity, cancellationToken).ConfigureAwait(false); if (result == null || result.IsDirectory.Value) - return NotFound(); + return StatusCode((int)HttpStatusCode.Gone); return Json(result); } @@ -109,7 +109,7 @@ namespace Tgstation.Server.Host.Controllers { var result = await instanceManager.GetInstance(Instance).Configuration.ListDirectory(path, AuthenticationContext.SystemIdentity, cancellationToken).ConfigureAwait(false); if (result == null) - return NotFound(); + return StatusCode((int)HttpStatusCode.Gone); return Json(result); } diff --git a/src/Tgstation.Server.Host/Core/Application.cs b/src/Tgstation.Server.Host/Core/Application.cs index 78abc75d9c..dd5f307a5d 100644 --- a/src/Tgstation.Server.Host/Core/Application.cs +++ b/src/Tgstation.Server.Host/Core/Application.cs @@ -148,7 +148,9 @@ namespace Tgstation.Server.Host.Core services.AddMvc().AddJsonOptions(options => { + options.AllowInputFormatterExceptionMessages = true; options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; + options.SerializerSettings.CheckAdditionalContent = true; }); var databaseConfiguration = databaseConfigurationSection.Get(); @@ -241,9 +243,8 @@ namespace Tgstation.Server.Host.Core logger.LogInformation(VersionString); serverAddresses = applicationBuilder.ServerFeatures.Get(); - - if (hostingEnvironment.IsDevelopment()) - applicationBuilder.UseDeveloperExceptionPage(); + + applicationBuilder.UseDeveloperExceptionPage(); //it is not worth it to limit this, you should only ever get it if you're an authorized user applicationBuilder.UseAsyncInitialization(async cancellationToken => { diff --git a/src/Tgstation.Server.Host/Security/TokenFactory.cs b/src/Tgstation.Server.Host/Security/TokenFactory.cs index 47b3809afe..e3603937bc 100644 --- a/src/Tgstation.Server.Host/Security/TokenFactory.cs +++ b/src/Tgstation.Server.Host/Security/TokenFactory.cs @@ -12,9 +12,9 @@ namespace Tgstation.Server.Host.Security sealed class TokenFactory : ITokenFactory { /// - /// Amount of hours until generated s expire + /// Amount of minutes until generated s expire /// - const int TokenExpiryHours = 1; + const int TokenExpiryMinutes = 15; public static readonly string TokenAudience = typeof(Token).Assembly.GetName().Name; public static readonly string TokenIssuer = Assembly.GetExecutingAssembly().GetName().Name; @@ -26,7 +26,7 @@ namespace Tgstation.Server.Host.Security if (user == null) throw new ArgumentNullException(nameof(user)); - expiry = DateTimeOffset.Now.AddHours(TokenExpiryHours); + expiry = DateTimeOffset.Now.AddMinutes(TokenExpiryMinutes); var claims = new Claim[] { new Claim(JwtRegisteredClaimNames.Sub, user.Id.ToString(CultureInfo.InvariantCulture)), diff --git a/tgstation-server.sln b/tgstation-server.sln index acd602d242..1b00a7a4f4 100644 --- a/tgstation-server.sln +++ b/tgstation-server.sln @@ -106,6 +106,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{A55C1117 tools\TGS.postman_collection.json = tools\TGS.postman_collection.json EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{DCC75431-7913-4306-9FAB-70998D440BCE}" + ProjectSection(SolutionItems) = preProject + docs\API.dox = docs\API.dox + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/tools/TGS.postman_collection.json b/tools/TGS.postman_collection.json index 70a76a745b..84428d142c 100644 --- a/tools/TGS.postman_collection.json +++ b/tools/TGS.postman_collection.json @@ -1,6 +1,6 @@ { "info": { - "_postman_id": "69222fa3-00a7-4bfe-91ad-a41a379d6181", + "_postman_id": "768127a5-f5e5-44f7-a6a1-7af42f975fb2", "name": "TGS", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" }, @@ -23,7 +23,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" } ], @@ -58,7 +58,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -97,8 +97,12 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" } ], "body": { @@ -160,8 +164,12 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" } ], "body": { @@ -195,8 +203,12 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" } ], "body": { @@ -231,7 +243,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -270,7 +282,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -309,7 +321,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -348,12 +360,16 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { "key": "Content-Type", "value": "application/json" + }, + { + "key": "Instance", + "value": "1" } ], "body": { @@ -387,12 +403,16 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { "key": "Content-Type", "value": "application/json" + }, + { + "key": "Instance", + "value": "1" } ], "body": { @@ -426,12 +446,16 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { "key": "Content-Type", "value": "application/json" + }, + { + "key": "Instance", + "value": "1" } ], "body": { @@ -465,12 +489,16 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { "key": "Content-Type", "value": "application/json" + }, + { + "key": "Instance", + "value": "1" } ], "body": { @@ -504,12 +532,16 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { "key": "Content-Type", "value": "application/json" + }, + { + "key": "Instance", + "value": "1" } ], "body": { @@ -546,7 +578,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -556,6 +588,10 @@ { "key": "Username", "value": "bob" + }, + { + "key": "Content-Type", + "value": "application/json" } ], "body": { @@ -589,7 +625,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -599,6 +635,10 @@ { "key": "Username", "value": "cyberboss" + }, + { + "key": "Content-Type", + "value": "application/json" } ], "body": { @@ -639,7 +679,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -647,7 +687,7 @@ "value": "application/json" }, { - "key": "InstanceId", + "key": "Instance", "value": "1" } ], @@ -682,7 +722,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -690,7 +730,7 @@ "value": "application/json" }, { - "key": "InstanceId", + "key": "Instance", "value": "1" } ], @@ -726,7 +766,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -734,7 +774,7 @@ "value": "application/json" }, { - "key": "InstanceId", + "key": "Instance", "value": "1" } ], @@ -770,7 +810,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -778,7 +818,7 @@ "value": "application/json" }, { - "key": "InstanceId", + "key": "Instance", "value": "1" } ], @@ -813,7 +853,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -821,7 +861,7 @@ "value": "application/json" }, { - "key": "InstanceId", + "key": "Instance", "value": "1" } ], @@ -856,7 +896,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -864,7 +904,7 @@ "value": "application/json" }, { - "key": "InstanceId", + "key": "Instance", "value": "1" } ], @@ -942,7 +982,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -950,7 +990,7 @@ "value": "application/json" }, { - "key": "InstanceId", + "key": "Instance", "value": "1" } ], @@ -985,7 +1025,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -993,7 +1033,7 @@ "value": "application/json" }, { - "key": "InstanceId", + "key": "Instance", "value": "1" } ], @@ -1028,7 +1068,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -1036,7 +1076,7 @@ "value": "application/json" }, { - "key": "InstanceId", + "key": "Instance", "value": "1" } ], @@ -1071,7 +1111,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -1079,7 +1119,7 @@ "value": "application/json" }, { - "key": "InstanceId", + "key": "Instance", "value": "1" } ], @@ -1115,7 +1155,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -1123,7 +1163,7 @@ "value": "application/json" }, { - "key": "InstanceId", + "key": "Instance", "value": "1" } ], @@ -1166,7 +1206,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -1174,7 +1214,7 @@ "value": "application/json" }, { - "key": "InstanceId", + "key": "Instance", "value": "1" } ], @@ -1209,7 +1249,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -1217,7 +1257,7 @@ "value": "application/json" }, { - "key": "InstanceId", + "key": "Instance", "value": "1" } ], @@ -1244,7 +1284,7 @@ }, { "name": "Jobs", - "description": "", + "description": null, "item": [ { "name": "List Ids", @@ -1260,7 +1300,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -1268,7 +1308,7 @@ "value": "application/json" }, { - "key": "InstanceId", + "key": "Instance", "value": "1" } ], @@ -1304,7 +1344,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -1312,7 +1352,7 @@ "value": "application/json" }, { - "key": "InstanceId", + "key": "Instance", "value": "1" } ], @@ -1348,7 +1388,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -1356,7 +1396,7 @@ "value": "application/json" }, { - "key": "InstanceId", + "key": "Instance", "value": "1" } ], @@ -1405,7 +1445,7 @@ }, { "name": "Byond", - "description": "", + "description": null, "item": [ { "name": "List installed versions", @@ -1421,7 +1461,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -1429,7 +1469,7 @@ "value": "application/json" }, { - "key": "InstanceId", + "key": "Instance", "value": "1" } ], @@ -1465,7 +1505,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -1473,7 +1513,7 @@ "value": "application/json" }, { - "key": "InstanceId", + "key": "Instance", "value": "1" } ], @@ -1508,7 +1548,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -1516,7 +1556,7 @@ "value": "application/json" }, { - "key": "InstanceId", + "key": "Instance", "value": "1" } ], @@ -1551,7 +1591,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -1559,7 +1599,7 @@ "value": "application/json" }, { - "key": "InstanceId", + "key": "Instance", "value": "1" } ], @@ -1585,7 +1625,7 @@ }, { "name": "Repo", - "description": "", + "description": null, "item": [ { "name": "Read Info", @@ -1601,7 +1641,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -1609,7 +1649,7 @@ "value": "application/json" }, { - "key": "InstanceId", + "key": "Instance", "value": "1" } ], @@ -1644,7 +1684,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -1652,7 +1692,7 @@ "value": "application/json" }, { - "key": "InstanceId", + "key": "Instance", "value": "1" } ], @@ -1687,7 +1727,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -1695,7 +1735,7 @@ "value": "application/json" }, { - "key": "InstanceId", + "key": "Instance", "value": "1" } ], @@ -1730,7 +1770,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -1738,7 +1778,7 @@ "value": "application/json" }, { - "key": "InstanceId", + "key": "Instance", "value": "1" } ], @@ -1773,7 +1813,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -1781,7 +1821,7 @@ "value": "application/json" }, { - "key": "InstanceId", + "key": "Instance", "value": "1" } ], @@ -1816,7 +1856,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -1824,7 +1864,7 @@ "value": "application/json" }, { - "key": "InstanceId", + "key": "Instance", "value": "1" } ], @@ -1859,7 +1899,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -1867,7 +1907,7 @@ "value": "application/json" }, { - "key": "InstanceId", + "key": "Instance", "value": "1" } ], @@ -1893,10 +1933,10 @@ }, { "name": "Compiler", - "description": "", + "description": null, "item": [ { - "name": "Read Info Copy", + "name": "Read", "request": { "method": "GET", "header": [ @@ -1909,7 +1949,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -1917,7 +1957,7 @@ "value": "application/json" }, { - "key": "InstanceId", + "key": "Instance", "value": "1" } ], @@ -1926,13 +1966,13 @@ "raw": "{\n \"id\": 1,\n \"online\": false\n}" }, "url": { - "raw": "localhost:5000/Repository", + "raw": "localhost:5000/DreamMaker", "host": [ "localhost" ], "port": "5000", "path": [ - "Repository" + "DreamMaker" ] } }, @@ -1955,7 +1995,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -1994,7 +2034,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -2033,7 +2073,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -2073,7 +2113,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -2083,7 +2123,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"name\": \"Test Instance\",\n \"path\": \"C:\\\\TestInstance\"\n}" + "raw": "{\n \"name\": \"Test Instance\",\n \"path\": \"D:\\\\TestInstance\"\n}" }, "url": { "raw": "localhost:5000/Instance", @@ -2112,7 +2152,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -2156,7 +2196,7 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" }, { @@ -2166,6 +2206,10 @@ { "key": "Username", "value": "admin" + }, + { + "key": "Content-Type", + "value": "application/json" } ], "body": { @@ -2196,8 +2240,12 @@ "value": "Postman/1.0" }, { - "key": "User-Agent", + "key": "Api", "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" } ], "body": { @@ -2241,7 +2289,7 @@ "const echoPostRequest = {", " url: 'http://localhost:5000',", " method: 'POST',", - " header: ['Accept:application/json', \"User-Agent:Postman/1.0\", \"User-Agent:Tgstation.Server.Api/4.0.0.0\", \"Authorization:Password ISolemlySwearToDeleteTheDataDirectory\", \"Username:admin\"]", + " header: ['Accept:application/json', \"User-Agent:Postman/1.0\", \"Api:Tgstation.Server.Api/4.0.0.0\", \"Authorization:Password ISolemlySwearToDeleteTheDataDirectory\", \"Username:admin\"]", "};", "", "pm.sendRequest(echoPostRequest, function (err, res) {",