Add ASF API swagger explorer

This commit is contained in:
JustArchi 2018-10-06 05:06:29 +02:00
parent 52a7bb048f
commit b15edf4559
21 changed files with 295 additions and 40 deletions

View file

@ -11,8 +11,9 @@
<Description>ASF is an application that allows you to farm steam cards using multiple steam accounts simultaneously.</Description>
<ErrorReport>none</ErrorReport>
<FileVersion>3.4.0.3</FileVersion>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<LangVersion>latest</LangVersion>
<NoWarn />
<NoWarn>1591</NoWarn>
<OutputType>Exe</OutputType>
<PackageIconUrl>https://github.com/JustArchi/ArchiSteamFarm/raw/master/resources/ASF.ico</PackageIconUrl>
<PackageLicenseUrl>http://www.apache.org/licenses/LICENSE-2.0</PackageLicenseUrl>
@ -54,6 +55,7 @@
<PackageReference Include="NLog" Version="4.5.10" />
<PackageReference Include="NLog.Web.AspNetCore" Version="4.7.0" />
<PackageReference Include="protobuf-net" Version="3.0.0-alpha.3" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="3.0.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp2.1'">

View file

@ -33,7 +33,7 @@ using SteamKit2;
namespace ArchiSteamFarm {
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
internal sealed class BotConfig {
public sealed class BotConfig {
private const bool DefaultAcceptGifts = false;
private const bool DefaultAutoSteamSaleEvent = false;
private const EBotBehaviour DefaultBotBehaviour = EBotBehaviour.None;

View file

@ -30,8 +30,12 @@ using Microsoft.AspNetCore.Mvc;
namespace ArchiSteamFarm.IPC.Controllers.Api {
[ApiController]
[Produces("application/json")]
[Route("Api/ASF")]
public sealed class ASFController : ControllerBase {
/// <summary>
/// Fetches common info related to ASF as a whole.
/// </summary>
[HttpGet]
public ActionResult<GenericResponse<ASFResponse>> ASFGet() {
uint memoryUsage = (uint) GC.GetTotalMemory(false) / 1024;
@ -46,6 +50,9 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
return Ok(new GenericResponse<ASFResponse>(result));
}
/// <summary>
/// Updates ASF's global config.
/// </summary>
[HttpPost]
public async Task<ActionResult<GenericResponse>> ASFPost([FromBody] ASFRequest request) {
if (request == null) {
@ -70,18 +77,27 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
return Ok(new GenericResponse(result));
}
/// <summary>
/// Makes ASF shutdown itself.
/// </summary>
[HttpPost("Exit")]
public ActionResult<GenericResponse> ExitPost() {
(bool success, string output) = Actions.Exit();
return Ok(new GenericResponse(success, output));
}
/// <summary>
/// Makes ASF restart itself.
/// </summary>
[HttpPost("Restart")]
public ActionResult<GenericResponse> RestartPost() {
(bool success, string output) = Actions.Restart();
return Ok(new GenericResponse(success, output));
}
/// <summary>
/// Makes ASF update itself.
/// </summary>
[HttpPost("Update")]
public async Task<ActionResult<GenericResponse<Version>>> UpdatePost() {
(bool success, Version version) = await Actions.Update().ConfigureAwait(false);

View file

@ -32,8 +32,12 @@ using Microsoft.AspNetCore.Mvc;
namespace ArchiSteamFarm.IPC.Controllers.Api {
[ApiController]
[Produces("application/json")]
[Route("Api/Bot")]
public sealed class BotController : ControllerBase {
/// <summary>
/// Deletes all files related to given bots.
/// </summary>
[HttpDelete("{botNames:required}")]
public async Task<ActionResult<GenericResponse>> BotDelete(string botNames) {
if (string.IsNullOrEmpty(botNames)) {
@ -50,6 +54,9 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
return Ok(new GenericResponse(results.All(result => result)));
}
/// <summary>
/// Fetches common info related to given bots.
/// </summary>
[HttpGet("{botNames:required}")]
public ActionResult<GenericResponse<HashSet<Bot>>> BotGet(string botNames) {
if (string.IsNullOrEmpty(botNames)) {
@ -65,6 +72,9 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
return Ok(new GenericResponse<HashSet<Bot>>(bots));
}
/// <summary>
/// Updates bot config of given bot.
/// </summary>
[HttpPost("{botName:required}")]
public async Task<ActionResult<GenericResponse>> BotPost(string botName, [FromBody] BotRequest request) {
if (string.IsNullOrEmpty(botName) || (request == null)) {
@ -99,6 +109,9 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
return Ok(new GenericResponse(result));
}
/// <summary>
/// Removes BGR output files of given bots.
/// </summary>
[HttpDelete("{botNames:required}/GamesToRedeemInBackground")]
public async Task<ActionResult<GenericResponse>> GamesToRedeemInBackgroundDelete(string botNames) {
if (string.IsNullOrEmpty(botNames)) {
@ -115,6 +128,9 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
return Ok(results.All(result => result) ? new GenericResponse(true) : new GenericResponse(false, Strings.WarningFailed));
}
/// <summary>
/// Fetches BGR output files of given bots.
/// </summary>
[HttpGet("{botNames:required}/GamesToRedeemInBackground")]
public async Task<ActionResult<GenericResponse<Dictionary<string, GamesToRedeemInBackgroundResponse>>>> GamesToRedeemInBackgroundGet(string botNames) {
if (string.IsNullOrEmpty(botNames)) {
@ -139,6 +155,9 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
return Ok(new GenericResponse<Dictionary<string, GamesToRedeemInBackgroundResponse>>(result));
}
/// <summary>
/// Adds keys to redeem using BGR to given bot.
/// </summary>
[HttpPost("{botName:required}/GamesToRedeemInBackground")]
public async Task<ActionResult<GenericResponse<OrderedDictionary>>> GamesToRedeemInBackgroundPost(string botName, [FromBody] GamesToRedeemInBackgroundRequest request) {
if (string.IsNullOrEmpty(botName) || (request == null)) {
@ -158,6 +177,9 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
return Ok(new GenericResponse<OrderedDictionary>(result, request.GamesToRedeemInBackground));
}
/// <summary>
/// Pauses given bots.
/// </summary>
[HttpPost("{botNames:required}/Pause")]
public async Task<ActionResult<GenericResponse>> PausePost(string botNames, [FromBody] BotPauseRequest request) {
if (string.IsNullOrEmpty(botNames) || (request == null)) {
@ -174,6 +196,9 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
return Ok(new GenericResponse(results.All(result => result.Success), string.Join(Environment.NewLine, results.Select(result => result.Output))));
}
/// <summary>
/// Resumes given bots.
/// </summary>
[HttpPost("{botNames:required}/Resume")]
public async Task<ActionResult<GenericResponse>> ResumePost(string botNames) {
if (string.IsNullOrEmpty(botNames)) {
@ -190,6 +215,9 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
return Ok(new GenericResponse(results.All(result => result.Success), string.Join(Environment.NewLine, results.Select(result => result.Output))));
}
/// <summary>
/// Starts given bots.
/// </summary>
[HttpPost("{botNames:required}/Start")]
public async Task<ActionResult<GenericResponse>> StartPost(string botNames) {
if (string.IsNullOrEmpty(botNames)) {
@ -206,6 +234,9 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
return Ok(new GenericResponse(results.All(result => result.Success), string.Join(Environment.NewLine, results.Select(result => result.Output))));
}
/// <summary>
/// Stops given bots.
/// </summary>
[HttpPost("{botNames:required}/Stop")]
public async Task<ActionResult<GenericResponse>> StopPost(string botNames) {
if (string.IsNullOrEmpty(botNames)) {

View file

@ -28,8 +28,16 @@ using Microsoft.AspNetCore.Mvc;
namespace ArchiSteamFarm.IPC.Controllers.Api {
[ApiController]
[Produces("application/json")]
[Route("Api/Command")]
public sealed class CommandController : ControllerBase {
/// <summary>
/// Executes a command.
/// </summary>
/// <remarks>
/// This API endpoint is supposed to be entirely replaced by ASF actions available under /Api/ASF/{action} and /Api/Bot/{bot}/{action}.
/// You should use "given bot" commands when executing this endpoint, omitting targets of the command will cause the command to be executed on first defined bot
/// </remarks>
[HttpPost("{command:required}")]
public async Task<ActionResult<GenericResponse<string>>> CommandPost(string command) {
if (string.IsNullOrEmpty(command)) {

View file

@ -33,10 +33,17 @@ using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Controllers.Api {
[ApiController]
[Produces("application/json")]
[Route("Api/NLog")]
public sealed class NLogController : ControllerBase {
private static readonly ConcurrentDictionary<WebSocket, SemaphoreSlim> ActiveLogWebSockets = new ConcurrentDictionary<WebSocket, SemaphoreSlim>();
/// <summary>
/// Fetches ASF log in realtime.
/// </summary>
/// <remarks>
/// This API endpoint requires a websocket connection.
/// </remarks>
[HttpGet]
public async Task<ActionResult> NLogGet() {
if (!HttpContext.WebSockets.IsWebSocketRequest) {

View file

@ -26,8 +26,15 @@ using Microsoft.AspNetCore.Mvc;
namespace ArchiSteamFarm.IPC.Controllers.Api {
[ApiController]
[Produces("application/json")]
[Route("Api/Structure")]
public sealed class StructureController : ControllerBase {
/// <summary>
/// Fetches structure of given type.
/// </summary>
/// <remarks>
/// Structure is defined as a representation of given object in its default state.
/// </remarks>
[HttpGet("{structure:required}")]
public ActionResult<GenericResponse<object>> StructureGet(string structure) {
if (string.IsNullOrEmpty(structure)) {

View file

@ -30,8 +30,15 @@ using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Controllers.Api {
[ApiController]
[Produces("application/json")]
[Route("Api/Type")]
public sealed class TypeController : ControllerBase {
/// <summary>
/// Fetches type info of given type.
/// </summary>
/// <remarks>
/// Type info is defined as a representation of given object with its fields and properties being assigned to a string value that defines their type.
/// </remarks>
[HttpGet("{type:required}")]
public ActionResult<GenericResponse<TypeResponse>> TypeGet(string type) {
if (string.IsNullOrEmpty(type)) {

View file

@ -31,8 +31,15 @@ using Microsoft.AspNetCore.Mvc;
namespace ArchiSteamFarm.IPC.Controllers.Api {
[ApiController]
[Produces("application/json")]
[Route("Api/WWW")]
public sealed class WWWController : ControllerBase {
/// <summary>
/// Fetches files in given directory relative to WWW root.
/// </summary>
/// <remarks>
/// This is internal API being utilizied by our ASF-ui IPC frontend. You should not depend on existence of any /Api/WWW as they can disappear and change anytime.
/// </remarks>
[HttpGet("Directory/{directory:required}")]
public ActionResult<GenericResponse<HashSet<string>>> DirectoryGet(string directory) {
if (string.IsNullOrEmpty(directory)) {
@ -57,6 +64,12 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
return Ok(new GenericResponse<HashSet<string>>(result));
}
/// <summary>
/// Fetches newest GitHub releases of ASF project.
/// </summary>
/// <remarks>
/// This is internal API being utilizied by our ASF-ui IPC frontend. You should not depend on existence of any /Api/WWW as they can disappear and change anytime.
/// </remarks>
[HttpGet("GitHub/Releases")]
public async Task<ActionResult<GenericResponse<IEnumerable<GitHubReleaseResponse>>>> GitHubReleasesGet([FromQuery] byte count = 10) {
if (count == 0) {
@ -72,6 +85,12 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
return Ok(new GenericResponse<IEnumerable<GitHubReleaseResponse>>(result));
}
/// <summary>
/// Fetches specific GitHub release of ASF project.
/// </summary>
/// <remarks>
/// This is internal API being utilizied by our ASF-ui IPC frontend. You should not depend on existence of any /Api/WWW as they can disappear and change anytime.
/// </remarks>
[HttpGet("GitHub/Releases/{version:required}")]
public async Task<ActionResult<GenericResponse<GitHubReleaseResponse>>> GitHubReleasesGet(string version) {
if (string.IsNullOrEmpty(version)) {
@ -86,6 +105,12 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
return Ok(new GenericResponse<GitHubReleaseResponse>(new GitHubReleaseResponse(releaseResponse)));
}
/// <summary>
/// Sends a HTTPS request through ASF's built-in HttpClient.
/// </summary>
/// <remarks>
/// This is internal API being utilizied by our ASF-ui IPC frontend. You should not depend on existence of any /Api/WWW as they can disappear and change anytime.
/// </remarks>
[HttpPost("Send")]
public async Task<ActionResult<GenericResponse<string>>> SendPost([FromBody] WWWSendRequest request) {
if (request == null) {

View file

@ -19,14 +19,19 @@
// See the License for the specific language governing permissions and
// limitations under the License.
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Requests {
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
public sealed class ASFRequest {
/// <summary>
/// ASF's global config structure.
/// </summary>
[JsonProperty(Required = Required.Always)]
internal readonly GlobalConfig GlobalConfig;
[Required]
public readonly GlobalConfig GlobalConfig;
// Deserialized from JSON
private ASFRequest() { }

View file

@ -25,11 +25,17 @@ using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Requests {
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
public sealed class BotPauseRequest {
/// <summary>
/// Specifies if pause is permanent or temporary (default).
/// </summary>
[JsonProperty(Required = Required.DisallowNull)]
internal readonly bool Permanent;
public readonly bool Permanent;
/// <summary>
/// Specifies automatic resume action in given seconds. Default value of 0 disables automatic resume.
/// </summary>
[JsonProperty(Required = Required.DisallowNull)]
internal readonly ushort ResumeInSeconds;
public readonly ushort ResumeInSeconds;
// Deserialized from JSON
private BotPauseRequest() { }

View file

@ -19,14 +19,19 @@
// See the License for the specific language governing permissions and
// limitations under the License.
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Requests {
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
public sealed class BotRequest {
/// <summary>
/// ASF's bot config structure.
/// </summary>
[JsonProperty(Required = Required.Always)]
internal readonly BotConfig BotConfig;
[Required]
public readonly BotConfig BotConfig;
// Deserialized from JSON
private BotRequest() { }

View file

@ -20,14 +20,23 @@
// limitations under the License.
using System.Collections.Specialized;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Requests {
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
public sealed class GamesToRedeemInBackgroundRequest {
/// <summary>
/// A string-string map that maps cd-key to redeem (key) to its name (value).
/// </summary>
/// <remarks>
/// Key in the map must be a valid and unique Steam cd-key.
/// Value in the map must be a non-null and non-empty name of the key (e.g. game's name, but can be anything).
/// </remarks>
[JsonProperty(Required = Required.Always)]
internal readonly OrderedDictionary GamesToRedeemInBackground;
[Required]
public readonly OrderedDictionary GamesToRedeemInBackground;
// Deserialized from JSON
private GamesToRedeemInBackgroundRequest() { }

View file

@ -19,14 +19,22 @@
// See the License for the specific language governing permissions and
// limitations under the License.
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Requests {
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
public sealed class WWWSendRequest {
/// <summary>
/// Full URL of the request to be made.
/// </summary>
/// <remarks>
/// URL must start from https:// scheme.
/// </remarks>
[Required]
[JsonProperty(Required = Required.Always)]
internal readonly string URL;
public readonly string URL;
// Deserialized from JSON
private WWWSendRequest() { }

View file

@ -20,24 +20,45 @@
// limitations under the License.
using System;
using System.ComponentModel.DataAnnotations;
using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Responses {
public sealed class ASFResponse {
[JsonProperty]
private readonly string BuildVariant;
/// <summary>
/// ASF's build variant.
/// </summary>
[JsonProperty(Required = Required.Always)]
[Required]
public readonly string BuildVariant;
[JsonProperty]
private readonly GlobalConfig GlobalConfig;
/// <summary>
/// Currently loaded ASF's global config.
/// </summary>
[JsonProperty(Required = Required.Always)]
[Required]
public readonly GlobalConfig GlobalConfig;
[JsonProperty]
private readonly uint MemoryUsage;
/// <summary>
/// Current amount of managed memory being used by the process, in kilobytes.
/// </summary>
[JsonProperty(Required = Required.Always)]
[Required]
public readonly uint MemoryUsage;
[JsonProperty]
private readonly DateTime ProcessStartTime;
/// <summary>
/// Start date of the process.
/// </summary>
[JsonProperty(Required = Required.Always)]
[Required]
public readonly DateTime ProcessStartTime;
[JsonProperty]
private readonly Version Version;
/// <summary>
/// ASF version of currently running binary.
/// </summary>
[JsonProperty(Required = Required.Always)]
[Required]
public readonly Version Version;
internal ASFResponse(string buildVariant, GlobalConfig globalConfig, uint memoryUsage, DateTime processStartTime, Version version) {
if (string.IsNullOrEmpty(buildVariant) || (globalConfig == null) || (memoryUsage == 0) || (processStartTime == DateTime.MinValue) || (version == null)) {

View file

@ -24,11 +24,17 @@ using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Responses {
public sealed class GamesToRedeemInBackgroundResponse {
/// <summary>
/// Keys that were redeemed and not used during the process, if available.
/// </summary>
[JsonProperty]
private readonly Dictionary<string, string> UnusedKeys;
public readonly Dictionary<string, string> UnusedKeys;
/// <summary>
/// Keys that were redeemed and used during the process, if available.
/// </summary>
[JsonProperty]
private readonly Dictionary<string, string> UsedKeys;
public readonly Dictionary<string, string> UsedKeys;
internal GamesToRedeemInBackgroundResponse(Dictionary<string, string> unusedKeys = null, Dictionary<string, string> usedKeys = null) {
UnusedKeys = unusedKeys;

View file

@ -19,13 +19,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.
using System.ComponentModel.DataAnnotations;
using ArchiSteamFarm.Localization;
using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Responses {
public sealed class GenericResponse<T> : GenericResponse where T : class {
/// <summary>
/// The actual result of the request, if available.
/// </summary>
/// <remarks>
/// The type of the result depends on the API endpoint that you've called.
/// </remarks>
[JsonProperty]
private readonly T Result;
public readonly T Result;
internal GenericResponse(T result) : base(result != null) => Result = result;
internal GenericResponse(bool success, string message) : base(success, message) { }
@ -33,11 +40,21 @@ namespace ArchiSteamFarm.IPC.Responses {
}
public class GenericResponse {
/// <summary>
/// A message that describes what happened with the request, if available.
/// </summary>
/// <remarks>
/// This property will provide exact reason for majority of expected failures.
/// </remarks>
[JsonProperty]
private readonly string Message;
public readonly string Message;
[JsonProperty]
private readonly bool Success;
/// <summary>
/// Boolean type that specifies if the request has succeeded.
/// </summary>
[JsonProperty(Required = Required.Always)]
[Required]
public readonly bool Success;
internal GenericResponse(bool success, string message = null) {
Success = success;

View file

@ -20,21 +20,38 @@
// limitations under the License.
using System;
using System.ComponentModel.DataAnnotations;
using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Responses {
public sealed class GitHubReleaseResponse {
[JsonProperty]
private readonly string ChangelogHTML;
/// <summary>
/// Changelog of the release rendered in HTML.
/// </summary>
[JsonProperty(Required = Required.Always)]
[Required]
public readonly string ChangelogHTML;
[JsonProperty]
private readonly DateTime ReleasedAt;
/// <summary>
/// Date of the release.
/// </summary>
[JsonProperty(Required = Required.Always)]
[Required]
public readonly DateTime ReleasedAt;
[JsonProperty]
private readonly bool Stable;
/// <summary>
/// Boolean value that specifies whether the build is stable or not (pre-release).
/// </summary>
[JsonProperty(Required = Required.Always)]
[Required]
public readonly bool Stable;
[JsonProperty]
private readonly string Version;
/// <summary>
/// Version of the release.
/// </summary>
[JsonProperty(Required = Required.Always)]
[Required]
public readonly string Version;
internal GitHubReleaseResponse(GitHub.ReleaseResponse releaseResponse) {
if (releaseResponse == null) {

View file

@ -21,15 +21,29 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Responses {
public sealed class TypeResponse {
[JsonProperty]
private readonly Dictionary<string, string> Body;
/// <summary>
/// A string-string map representing a decomposition of given type.
/// </summary>
/// <remarks>
/// The actual structure of this field depends on the type that was requested. You can determine that type based on <see cref="Properties"/> metadata.
/// For enums, keys are friendly names while values are underlying values of those names.
/// For objects, keys are non-private fields and properties, while values are underlying types of those.
/// </remarks>
[JsonProperty(Required = Required.Always)]
[Required]
public readonly Dictionary<string, string> Body;
[JsonProperty]
private readonly TypeProperties Properties;
/// <summary>
/// Metadata of given type.
/// </summary>
[JsonProperty(Required = Required.Always)]
[Required]
public readonly TypeProperties Properties;
internal TypeResponse(Dictionary<string, string> body, TypeProperties properties) {
if ((body == null) || (properties == null)) {
@ -40,15 +54,33 @@ namespace ArchiSteamFarm.IPC.Responses {
Properties = properties;
}
internal sealed class TypeProperties {
public sealed class TypeProperties {
/// <summary>
/// Base type of given type, if available.
/// </summary>
/// <remarks>
/// This can be used for determining how <see cref="Body"/> should be interpreted.
/// </remarks>
[JsonProperty]
private readonly string BaseType;
public readonly string BaseType;
/// <summary>
/// Custom attributes of given type, if available.
/// </summary>
/// <remarks>
/// This can be used for determining main enum type if <see cref="BaseType"/> is <see cref="Enum"/>.
/// </remarks>
[JsonProperty]
private readonly HashSet<string> CustomAttributes;
public readonly HashSet<string> CustomAttributes;
/// <summary>
/// Underlying type of given type, if available.
/// </summary>
/// <remarks>
/// This can be used for determining underlying enum type if <see cref="BaseType"/> is <see cref="Enum"/>.
/// </remarks>
[JsonProperty]
private readonly string UnderlyingType;
public readonly string UnderlyingType;
internal TypeProperties(string baseType = null, HashSet<string> customAttributes = null, string underlyingType = null) {
BaseType = baseType;

View file

@ -20,6 +20,7 @@
// limitations under the License.
using System;
using System.IO;
using ArchiSteamFarm.IPC.Middleware;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
@ -29,6 +30,7 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Swashbuckle.AspNetCore.Swagger;
namespace ArchiSteamFarm.IPC {
internal sealed class Startup {
@ -64,6 +66,12 @@ namespace ArchiSteamFarm.IPC {
// We need MVC for /Api
app.UseMvcWithDefaultRoute();
// Use swagger for automatic API documentation generation
app.UseSwagger();
// Use friendly swagger UI
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/ASF/swagger.json", "ASF API"));
// We're using index for URL routing in our static files so re-execute all non-API calls on /
app.UseWhen(context => !context.Request.Path.StartsWithSegments("/Api", StringComparison.OrdinalIgnoreCase), appBuilder => appBuilder.UseStatusCodePagesWithReExecute("/"));
@ -83,9 +91,26 @@ namespace ArchiSteamFarm.IPC {
// Add support for response compression
services.AddResponseCompression();
// Add swagger documentation generation
services.AddSwaggerGen(
c => {
c.DescribeAllEnumsAsStrings();
c.SwaggerDoc("ASF", new Info { Title = "ASF API" });
string xmlDocumentationFile = Path.Combine(AppContext.BaseDirectory, SharedInfo.AssemblyDocumentation);
if (File.Exists(xmlDocumentationFile)) {
c.IncludeXmlComments(xmlDocumentationFile);
}
}
);
// We need MVC for /Api, but we're going to use only a small subset of all available features
IMvcCoreBuilder mvc = services.AddMvcCore();
// Add API explorer for swagger
mvc.AddApiExplorer();
// Use latest compatibility version for MVC
mvc.SetCompatibilityVersion(CompatibilityVersion.Latest);

View file

@ -29,6 +29,7 @@ namespace ArchiSteamFarm {
internal const ulong ArchiSteamID = 76561198006963719;
internal const string ASF = nameof(ASF);
internal const ulong ASFGroupSteamID = 103582791440160998;
internal const string AssemblyDocumentation = AssemblyName + ".xml";
internal const string AssemblyName = nameof(ArchiSteamFarm);
internal const string ConfigDirectory = "config";
internal const string ConfigExtension = ".json";