IPC controllers cleanup

This commit is contained in:
JustArchi
2019-04-11 22:47:21 +02:00
parent ac70af0268
commit 513b821b60
9 changed files with 119 additions and 89 deletions

View File

@@ -37,7 +37,7 @@ namespace ArchiSteamFarm.CustomPlugins.ExamplePlugin {
[HttpGet]
[ProducesResponseType(typeof(GenericResponse<string>), (int) HttpStatusCode.OK)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.ServiceUnavailable)]
public async Task<ActionResult<GenericResponse<string>>> CatGet() {
public async Task<ActionResult<GenericResponse>> CatGet() {
string link = await CatAPI.GetRandomCatURL(ASF.WebBrowser).ConfigureAwait(false);
return !string.IsNullOrEmpty(link) ? Ok(new GenericResponse<string>(link)) : StatusCode((int) HttpStatusCode.ServiceUnavailable, new GenericResponse(false));

View File

@@ -22,6 +22,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using ArchiSteamFarm.IPC.Requests;
using ArchiSteamFarm.IPC.Responses;
@@ -36,7 +37,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// Fetches common info related to ASF as a whole.
/// </summary>
[HttpGet]
[ProducesResponseType(typeof(GenericResponse<ASFResponse>), 200)]
[ProducesResponseType(typeof(GenericResponse<ASFResponse>), (int) HttpStatusCode.OK)]
public ActionResult<GenericResponse<ASFResponse>> ASFGet() {
uint memoryUsage = (uint) GC.GetTotalMemory(false) / 1024;
@@ -50,7 +51,8 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// </summary>
[Consumes("application/json")]
[HttpPost]
[ProducesResponseType(typeof(GenericResponse), 200)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.OK)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)]
public async Task<ActionResult<GenericResponse>> ASFPost([FromBody] ASFRequest request) {
if (request == null) {
ASF.ArchiLogger.LogNullError(nameof(request));
@@ -100,7 +102,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// Makes ASF shutdown itself.
/// </summary>
[HttpPost("Exit")]
[ProducesResponseType(typeof(GenericResponse), 200)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.OK)]
public ActionResult<GenericResponse> ExitPost() {
(bool success, string message) = Actions.Exit();
@@ -111,7 +113,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// Makes ASF restart itself.
/// </summary>
[HttpPost("Restart")]
[ProducesResponseType(typeof(GenericResponse), 200)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.OK)]
public ActionResult<GenericResponse> RestartPost() {
(bool success, string message) = Actions.Restart();
@@ -122,7 +124,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// Makes ASF update itself.
/// </summary>
[HttpPost("Update")]
[ProducesResponseType(typeof(GenericResponse<Version>), 200)]
[ProducesResponseType(typeof(GenericResponse<Version>), (int) HttpStatusCode.OK)]
public async Task<ActionResult<GenericResponse<Version>>> UpdatePost() {
(bool success, string message, Version version) = await Actions.Update().ConfigureAwait(false);

View File

@@ -19,6 +19,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
using System.Net;
using ArchiSteamFarm.IPC.Responses;
using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;
@@ -26,8 +27,10 @@ using Swashbuckle.AspNetCore.Annotations;
namespace ArchiSteamFarm.IPC.Controllers.Api {
[ApiController]
[Produces("application/json")]
[SwaggerResponse(400, "The request has failed, check " + nameof(GenericResponse.Message) + " from response body for actual reason. Most of the time this is ASF, understanding the request, but refusing to execute it due to provided reason.", typeof(GenericResponse))]
[SwaggerResponse(401, "ASF has " + nameof(GlobalConfig.IPCPassword) + " set, but you've failed to authenticate. See " + SharedInfo.ProjectURL + "/wiki/IPC#authentication.")]
[SwaggerResponse(403, "ASF has " + nameof(GlobalConfig.IPCPassword) + " set and you've failed to authenticate too many times, try again in an hour. See " + SharedInfo.ProjectURL + "/wiki/IPC#authentication.")]
[SwaggerResponse((int) HttpStatusCode.BadRequest, "The request has failed, check " + nameof(GenericResponse.Message) + " from response body for actual reason. Most of the time this is ASF, understanding the request, but refusing to execute it due to provided reason.", typeof(GenericResponse))]
[SwaggerResponse((int) HttpStatusCode.Unauthorized, "ASF has " + nameof(GlobalConfig.IPCPassword) + " set, but you've failed to authenticate. See " + SharedInfo.ProjectURL + "/wiki/IPC#authentication.")]
[SwaggerResponse((int) HttpStatusCode.Forbidden, "ASF has " + nameof(GlobalConfig.IPCPassword) + " set and you've failed to authenticate too many times, try again in an hour. See " + SharedInfo.ProjectURL + "/wiki/IPC#authentication.")]
[SwaggerResponse((int) HttpStatusCode.InternalServerError, "ASF has encountered an unexpected error while serving the request. The log may include extra info related to this issue.")]
[SwaggerResponse((int) HttpStatusCode.ServiceUnavailable, "ASF has encountered an error while requesting a third-party resource. Try again later.")]
public abstract class ArchiController : ControllerBase { }
}

View File

@@ -23,6 +23,7 @@ using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using ArchiSteamFarm.IPC.Requests;
using ArchiSteamFarm.IPC.Responses;
@@ -37,7 +38,8 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// Deletes all files related to given bots.
/// </summary>
[HttpDelete("{botNames:required}")]
[ProducesResponseType(typeof(GenericResponse), 200)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.OK)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)]
public async Task<ActionResult<GenericResponse>> BotDelete(string botNames) {
if (string.IsNullOrEmpty(botNames)) {
ASF.ArchiLogger.LogNullError(nameof(botNames));
@@ -60,18 +62,19 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// Fetches common info related to given bots.
/// </summary>
[HttpGet("{botNames:required}")]
[ProducesResponseType(typeof(GenericResponse<IReadOnlyDictionary<string, Bot>>), 200)]
public ActionResult<GenericResponse<IReadOnlyDictionary<string, Bot>>> BotGet(string botNames) {
[ProducesResponseType(typeof(GenericResponse<IReadOnlyDictionary<string, Bot>>), (int) HttpStatusCode.OK)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)]
public ActionResult<GenericResponse> BotGet(string botNames) {
if (string.IsNullOrEmpty(botNames)) {
ASF.ArchiLogger.LogNullError(nameof(botNames));
return BadRequest(new GenericResponse<IReadOnlyDictionary<string, Bot>>(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames))));
return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames))));
}
HashSet<Bot> bots = Bot.GetBots(botNames);
if (bots == null) {
return BadRequest(new GenericResponse<IReadOnlyDictionary<string, Bot>>(false, string.Format(Strings.ErrorIsInvalid, nameof(bots))));
return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsInvalid, nameof(bots))));
}
return Ok(new GenericResponse<IReadOnlyDictionary<string, Bot>>(bots.ToDictionary(bot => bot.BotName, bot => bot, Bot.BotsComparer)));
@@ -82,18 +85,19 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// </summary>
[Consumes("application/json")]
[HttpPost("{botNames:required}")]
[ProducesResponseType(typeof(GenericResponse<IReadOnlyDictionary<string, bool>>), 200)]
public async Task<ActionResult<GenericResponse<IReadOnlyDictionary<string, bool>>>> BotPost(string botNames, [FromBody] BotRequest request) {
[ProducesResponseType(typeof(GenericResponse<IReadOnlyDictionary<string, bool>>), (int) HttpStatusCode.OK)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)]
public async Task<ActionResult<GenericResponse>> BotPost(string botNames, [FromBody] BotRequest request) {
if (string.IsNullOrEmpty(botNames) || (request == null)) {
ASF.ArchiLogger.LogNullError(nameof(botNames) + " || " + nameof(request));
return BadRequest(new GenericResponse<IReadOnlyDictionary<string, bool>>(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames) + " || " + nameof(request))));
return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames) + " || " + nameof(request))));
}
(bool valid, string errorMessage) = request.BotConfig.CheckValidation();
if (!valid) {
return BadRequest(new GenericResponse<IReadOnlyDictionary<string, bool>>(false, errorMessage));
return BadRequest(new GenericResponse(false, errorMessage));
}
request.BotConfig.ShouldSerializeEverything = false;
@@ -135,7 +139,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
if (string.IsNullOrEmpty(filePath)) {
ASF.ArchiLogger.LogNullError(filePath);
return BadRequest(new GenericResponse<IReadOnlyDictionary<string, bool>>(false, string.Format(Strings.ErrorIsInvalid, nameof(filePath))));
return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsInvalid, nameof(filePath))));
}
result[botName] = await BotConfig.Write(filePath, request.BotConfig).ConfigureAwait(false);
@@ -148,7 +152,8 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// Removes BGR output files of given bots.
/// </summary>
[HttpDelete("{botNames:required}/GamesToRedeemInBackground")]
[ProducesResponseType(typeof(GenericResponse), 200)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.OK)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)]
public async Task<ActionResult<GenericResponse>> GamesToRedeemInBackgroundDelete(string botNames) {
if (string.IsNullOrEmpty(botNames)) {
ASF.ArchiLogger.LogNullError(nameof(botNames));
@@ -171,18 +176,19 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// Fetches BGR output files of given bots.
/// </summary>
[HttpGet("{botNames:required}/GamesToRedeemInBackground")]
[ProducesResponseType(typeof(GenericResponse<IReadOnlyDictionary<string, GamesToRedeemInBackgroundResponse>>), 200)]
public async Task<ActionResult<GenericResponse<IReadOnlyDictionary<string, GamesToRedeemInBackgroundResponse>>>> GamesToRedeemInBackgroundGet(string botNames) {
[ProducesResponseType(typeof(GenericResponse<IReadOnlyDictionary<string, GamesToRedeemInBackgroundResponse>>), (int) HttpStatusCode.OK)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)]
public async Task<ActionResult<GenericResponse>> GamesToRedeemInBackgroundGet(string botNames) {
if (string.IsNullOrEmpty(botNames)) {
ASF.ArchiLogger.LogNullError(nameof(botNames));
return BadRequest(new GenericResponse<IReadOnlyDictionary<string, GamesToRedeemInBackgroundResponse>>(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames))));
return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames))));
}
HashSet<Bot> bots = Bot.GetBots(botNames);
if ((bots == null) || (bots.Count == 0)) {
return BadRequest(new GenericResponse<IReadOnlyDictionary<string, GamesToRedeemInBackgroundResponse>>(false, string.Format(Strings.BotNotFound, botNames)));
return BadRequest(new GenericResponse(false, string.Format(Strings.BotNotFound, botNames)));
}
IList<(Dictionary<string, string> UnusedKeys, Dictionary<string, string> UsedKeys)> results = await Utilities.InParallel(bots.Select(bot => bot.GetUsedAndUnusedKeys())).ConfigureAwait(false);
@@ -202,28 +208,29 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// </summary>
[Consumes("application/json")]
[HttpPost("{botNames:required}/GamesToRedeemInBackground")]
[ProducesResponseType(typeof(GenericResponse<IReadOnlyDictionary<string, IOrderedDictionary>>), 200)]
public async Task<ActionResult<GenericResponse<IReadOnlyDictionary<string, IOrderedDictionary>>>> GamesToRedeemInBackgroundPost(string botNames, [FromBody] BotGamesToRedeemInBackgroundRequest request) {
[ProducesResponseType(typeof(GenericResponse<IReadOnlyDictionary<string, IOrderedDictionary>>), (int) HttpStatusCode.OK)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)]
public async Task<ActionResult<GenericResponse>> GamesToRedeemInBackgroundPost(string botNames, [FromBody] BotGamesToRedeemInBackgroundRequest request) {
if (string.IsNullOrEmpty(botNames) || (request == null)) {
ASF.ArchiLogger.LogNullError(nameof(botNames) + " || " + nameof(request));
return BadRequest(new GenericResponse<IReadOnlyDictionary<string, IOrderedDictionary>>(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames) + " || " + nameof(request))));
return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames) + " || " + nameof(request))));
}
if (request.GamesToRedeemInBackground.Count == 0) {
return BadRequest(new GenericResponse<IReadOnlyDictionary<string, IOrderedDictionary>>(false, string.Format(Strings.ErrorIsEmpty, nameof(request.GamesToRedeemInBackground))));
return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(request.GamesToRedeemInBackground))));
}
HashSet<Bot> bots = Bot.GetBots(botNames);
if ((bots == null) || (bots.Count == 0)) {
return BadRequest(new GenericResponse<IReadOnlyDictionary<string, IOrderedDictionary>>(false, string.Format(Strings.BotNotFound, botNames)));
return BadRequest(new GenericResponse(false, string.Format(Strings.BotNotFound, botNames)));
}
IOrderedDictionary validGamesToRedeemInBackground = Bot.ValidateGamesToRedeemInBackground(request.GamesToRedeemInBackground);
if ((validGamesToRedeemInBackground == null) || (validGamesToRedeemInBackground.Count == 0)) {
return BadRequest(new GenericResponse<IReadOnlyDictionary<string, IOrderedDictionary>>(false, string.Format(Strings.ErrorIsEmpty, nameof(validGamesToRedeemInBackground))));
return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(validGamesToRedeemInBackground))));
}
await Utilities.InParallel(bots.Select(bot => bot.AddGamesToRedeemInBackground(validGamesToRedeemInBackground))).ConfigureAwait(false);
@@ -242,7 +249,8 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// </summary>
[Consumes("application/json")]
[HttpPost("{botNames:required}/Pause")]
[ProducesResponseType(typeof(GenericResponse), 200)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.OK)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)]
public async Task<ActionResult<GenericResponse>> PausePost(string botNames, [FromBody] BotPauseRequest request) {
if (string.IsNullOrEmpty(botNames) || (request == null)) {
ASF.ArchiLogger.LogNullError(nameof(botNames) + " || " + nameof(request));
@@ -270,22 +278,23 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// </remarks>
[Consumes("application/json")]
[HttpPost("{botNames:required}/Redeem")]
[ProducesResponseType(typeof(GenericResponse<IReadOnlyDictionary<string, IReadOnlyDictionary<string, ArchiHandler.PurchaseResponseCallback>>>), 200)]
public async Task<ActionResult<GenericResponse<IReadOnlyDictionary<string, IReadOnlyDictionary<string, ArchiHandler.PurchaseResponseCallback>>>>> RedeemPost(string botNames, [FromBody] BotRedeemRequest request) {
[ProducesResponseType(typeof(GenericResponse<IReadOnlyDictionary<string, IReadOnlyDictionary<string, ArchiHandler.PurchaseResponseCallback>>>), (int) HttpStatusCode.OK)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)]
public async Task<ActionResult<GenericResponse>> RedeemPost(string botNames, [FromBody] BotRedeemRequest request) {
if (string.IsNullOrEmpty(botNames) || (request == null)) {
ASF.ArchiLogger.LogNullError(nameof(botNames) + " || " + nameof(request));
return BadRequest(new GenericResponse<IReadOnlyDictionary<string, IReadOnlyDictionary<string, ArchiHandler.PurchaseResponseCallback>>>(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames) + " || " + nameof(request))));
return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames) + " || " + nameof(request))));
}
if (request.KeysToRedeem.Count == 0) {
return BadRequest(new GenericResponse<IReadOnlyDictionary<string, IReadOnlyDictionary<string, ArchiHandler.PurchaseResponseCallback>>>(false, string.Format(Strings.ErrorIsEmpty, nameof(request.KeysToRedeem))));
return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(request.KeysToRedeem))));
}
HashSet<Bot> bots = Bot.GetBots(botNames);
if ((bots == null) || (bots.Count == 0)) {
return BadRequest(new GenericResponse<IReadOnlyDictionary<string, IReadOnlyDictionary<string, ArchiHandler.PurchaseResponseCallback>>>(false, string.Format(Strings.BotNotFound, botNames)));
return BadRequest(new GenericResponse(false, string.Format(Strings.BotNotFound, botNames)));
}
IList<ArchiHandler.PurchaseResponseCallback> results = await Utilities.InParallel(bots.Select(bot => request.KeysToRedeem.Select(key => bot.Actions.RedeemKey(key))).SelectMany(task => task)).ConfigureAwait(false);
@@ -311,7 +320,8 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// </summary>
[Consumes("application/json")]
[HttpPost("{botName:required}/Rename")]
[ProducesResponseType(typeof(GenericResponse), 200)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.OK)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)]
public async Task<ActionResult<GenericResponse>> RenamePost(string botName, [FromBody] BotRenameRequest request) {
if (string.IsNullOrEmpty(botName) || (request == null)) {
ASF.ArchiLogger.LogNullError(nameof(botName) + " || " + nameof(request));
@@ -336,7 +346,8 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// Resumes given bots.
/// </summary>
[HttpPost("{botNames:required}/Resume")]
[ProducesResponseType(typeof(GenericResponse), 200)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.OK)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)]
public async Task<ActionResult<GenericResponse>> ResumePost(string botNames) {
if (string.IsNullOrEmpty(botNames)) {
ASF.ArchiLogger.LogNullError(nameof(botNames));
@@ -359,7 +370,8 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// Starts given bots.
/// </summary>
[HttpPost("{botNames:required}/Start")]
[ProducesResponseType(typeof(GenericResponse), 200)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.OK)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)]
public async Task<ActionResult<GenericResponse>> StartPost(string botNames) {
if (string.IsNullOrEmpty(botNames)) {
ASF.ArchiLogger.LogNullError(nameof(botNames));
@@ -382,7 +394,8 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// Stops given bots.
/// </summary>
[HttpPost("{botNames:required}/Stop")]
[ProducesResponseType(typeof(GenericResponse), 200)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.OK)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)]
public async Task<ActionResult<GenericResponse>> StopPost(string botNames) {
if (string.IsNullOrEmpty(botNames)) {
ASF.ArchiLogger.LogNullError(nameof(botNames));
@@ -405,22 +418,25 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// Accepts 2FA confirmations of given bots, requires ASF 2FA module to be active on them.
/// </summary>
[HttpPost("{botNames:required}/TwoFactorAuthentication/Confirmations/Accept")]
[ProducesResponseType(typeof(GenericResponse<IReadOnlyDictionary<string, GenericResponse>>), 200)]
public async Task<ActionResult<GenericResponse<IReadOnlyDictionary<string, GenericResponse>>>> TwoFactorAuthenticationConfirmationsAcceptPost(string botNames) => await TwoFactorAuthenticationConfirmationsPost(botNames, true).ConfigureAwait(false);
[ProducesResponseType(typeof(GenericResponse<IReadOnlyDictionary<string, GenericResponse>>), (int) HttpStatusCode.OK)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)]
public async Task<ActionResult<GenericResponse>> TwoFactorAuthenticationConfirmationsAcceptPost(string botNames) => await TwoFactorAuthenticationConfirmationsPost(botNames, true).ConfigureAwait(false);
/// <summary>
/// Denies 2FA confirmations of given bots, requires ASF 2FA module to be active on them.
/// </summary>
[HttpPost("{botNames:required}/TwoFactorAuthentication/Confirmations/Cancel")]
[ProducesResponseType(typeof(GenericResponse<IReadOnlyDictionary<string, GenericResponse>>), 200)]
public async Task<ActionResult<GenericResponse<IReadOnlyDictionary<string, GenericResponse>>>> TwoFactorAuthenticationConfirmationsCancelPost(string botNames) => await TwoFactorAuthenticationConfirmationsPost(botNames, false).ConfigureAwait(false);
[ProducesResponseType(typeof(GenericResponse<IReadOnlyDictionary<string, GenericResponse>>), (int) HttpStatusCode.OK)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)]
public async Task<ActionResult<GenericResponse>> TwoFactorAuthenticationConfirmationsCancelPost(string botNames) => await TwoFactorAuthenticationConfirmationsPost(botNames, false).ConfigureAwait(false);
/// <summary>
/// Fetches 2FA tokens of given bots, requires ASF 2FA module to be active on them.
/// </summary>
[HttpGet("{botNames:required}/TwoFactorAuthentication/Token")]
[ProducesResponseType(typeof(GenericResponse<IReadOnlyDictionary<string, GenericResponse<string>>>), 200)]
public async Task<ActionResult<GenericResponse<IReadOnlyDictionary<string, GenericResponse<string>>>>> TwoFactorAuthenticationTokenGet(string botNames) {
[ProducesResponseType(typeof(GenericResponse<IReadOnlyDictionary<string, GenericResponse<string>>>), (int) HttpStatusCode.OK)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)]
public async Task<ActionResult<GenericResponse>> TwoFactorAuthenticationTokenGet(string botNames) {
if (string.IsNullOrEmpty(botNames)) {
ASF.ArchiLogger.LogNullError(nameof(botNames));
@@ -445,7 +461,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
return Ok(new GenericResponse<IReadOnlyDictionary<string, GenericResponse<string>>>(result));
}
private async Task<ActionResult<GenericResponse<IReadOnlyDictionary<string, GenericResponse>>>> TwoFactorAuthenticationConfirmationsPost(string botNames, bool accept) {
private async Task<ActionResult<GenericResponse>> TwoFactorAuthenticationConfirmationsPost(string botNames, bool accept) {
if (string.IsNullOrEmpty(botNames)) {
ASF.ArchiLogger.LogNullError(nameof(botNames));

View File

@@ -21,6 +21,7 @@
using System;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using ArchiSteamFarm.IPC.Responses;
using ArchiSteamFarm.Localization;
@@ -37,29 +38,30 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// 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}")]
[ProducesResponseType(typeof(GenericResponse<string>), 200)]
public async Task<ActionResult<GenericResponse<string>>> CommandPost(string command) {
[ProducesResponseType(typeof(GenericResponse<string>), (int) HttpStatusCode.OK)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)]
public async Task<ActionResult<GenericResponse>> CommandPost(string command) {
if (string.IsNullOrEmpty(command)) {
ASF.ArchiLogger.LogNullError(nameof(command));
return BadRequest(new GenericResponse<string>(false, string.Format(Strings.ErrorIsEmpty, nameof(command))));
return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(command))));
}
if (ASF.GlobalConfig.SteamOwnerID == 0) {
return BadRequest(new GenericResponse<string>(false, string.Format(Strings.ErrorIsInvalid, nameof(ASF.GlobalConfig.SteamOwnerID))));
return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsInvalid, nameof(ASF.GlobalConfig.SteamOwnerID))));
}
Bot targetBot = Bot.Bots.OrderBy(bot => bot.Key, Bot.BotsComparer).Select(bot => bot.Value).FirstOrDefault();
if (targetBot == null) {
return BadRequest(new GenericResponse<string>(false, Strings.ErrorNoBotsDefined));
return BadRequest(new GenericResponse(false, Strings.ErrorNoBotsDefined));
}
if (!string.IsNullOrEmpty(ASF.GlobalConfig.CommandPrefix) && command.StartsWith(ASF.GlobalConfig.CommandPrefix, StringComparison.Ordinal)) {
command = command.Substring(ASF.GlobalConfig.CommandPrefix.Length);
if (string.IsNullOrEmpty(command)) {
return BadRequest(new GenericResponse<string>(false, string.Format(Strings.ErrorIsEmpty, nameof(command))));
return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(command))));
}
}

View File

@@ -22,6 +22,7 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
@@ -44,7 +45,8 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// This API endpoint requires a websocket connection.
/// </remarks>
[HttpGet]
[ProducesResponseType(typeof(IEnumerable<GenericResponse<string>>), 200)]
[ProducesResponseType(typeof(IEnumerable<GenericResponse<string>>), (int) HttpStatusCode.OK)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)]
public async Task<ActionResult> NLogGet() {
if (!HttpContext.WebSockets.IsWebSocketRequest) {
return BadRequest(new GenericResponse(false, string.Format(Strings.WarningFailedWithError, nameof(HttpContext.WebSockets.IsWebSocketRequest) + ": " + HttpContext.WebSockets.IsWebSocketRequest)));

View File

@@ -20,6 +20,7 @@
// limitations under the License.
using System;
using System.Net;
using ArchiSteamFarm.IPC.Responses;
using ArchiSteamFarm.Localization;
using Microsoft.AspNetCore.Mvc;
@@ -34,18 +35,19 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// Structure is defined as a representation of given object in its default state.
/// </remarks>
[HttpGet("{structure:required}")]
[ProducesResponseType(typeof(GenericResponse<object>), 200)]
public ActionResult<GenericResponse<object>> StructureGet(string structure) {
[ProducesResponseType(typeof(GenericResponse<object>), (int) HttpStatusCode.OK)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)]
public ActionResult<GenericResponse> StructureGet(string structure) {
if (string.IsNullOrEmpty(structure)) {
ASF.ArchiLogger.LogNullError(nameof(structure));
return BadRequest(new GenericResponse<object>(false, string.Format(Strings.ErrorIsEmpty, nameof(structure))));
return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(structure))));
}
Type targetType = WebUtilities.ParseType(structure);
if (targetType == null) {
return BadRequest(new GenericResponse<object>(false, string.Format(Strings.ErrorIsInvalid, structure)));
return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsInvalid, structure)));
}
object obj;
@@ -53,7 +55,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
try {
obj = Activator.CreateInstance(targetType, true);
} catch (Exception e) {
return BadRequest(new GenericResponse<object>(false, string.Format(Strings.ErrorParsingObject, nameof(targetType)) + Environment.NewLine + e));
return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorParsingObject, nameof(targetType)) + Environment.NewLine + e));
}
return Ok(new GenericResponse<object>(obj));

View File

@@ -22,6 +22,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Reflection;
using ArchiSteamFarm.IPC.Responses;
using ArchiSteamFarm.Localization;
@@ -38,18 +39,19 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// 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}")]
[ProducesResponseType(typeof(GenericResponse<TypeResponse>), 200)]
public ActionResult<GenericResponse<TypeResponse>> TypeGet(string type) {
[ProducesResponseType(typeof(GenericResponse<TypeResponse>), (int) HttpStatusCode.OK)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)]
public ActionResult<GenericResponse> TypeGet(string type) {
if (string.IsNullOrEmpty(type)) {
ASF.ArchiLogger.LogNullError(nameof(type));
return BadRequest(new GenericResponse<TypeResponse>(false, string.Format(Strings.ErrorIsEmpty, nameof(type))));
return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(type))));
}
Type targetType = WebUtilities.ParseType(type);
if (targetType == null) {
return BadRequest(new GenericResponse<object>(false, string.Format(Strings.ErrorIsInvalid, type)));
return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsInvalid, type)));
}
string baseType = targetType.BaseType?.GetUnifiedName();

View File

@@ -24,6 +24,7 @@ using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using ArchiSteamFarm.IPC.Requests;
using ArchiSteamFarm.IPC.Responses;
@@ -40,18 +41,20 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// This is internal API being utilizied by our ASF-ui IPC frontend. You should not depend on existence of any /Api/WWW endpoints as they can disappear and change anytime.
/// </remarks>
[HttpGet("Directory/{directory:required}")]
[ProducesResponseType(typeof(GenericResponse<IReadOnlyCollection<string>>), 200)]
public ActionResult<GenericResponse<IReadOnlyCollection<string>>> DirectoryGet(string directory) {
[ProducesResponseType(typeof(GenericResponse<IReadOnlyCollection<string>>), (int) HttpStatusCode.OK)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.InternalServerError)]
public ActionResult<GenericResponse> DirectoryGet(string directory) {
if (string.IsNullOrEmpty(directory)) {
ASF.ArchiLogger.LogNullError(nameof(directory));
return BadRequest(new GenericResponse<IReadOnlyCollection<string>>(false, string.Format(Strings.ErrorIsEmpty, nameof(directory))));
return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(directory))));
}
string directoryPath = Path.Combine(ArchiKestrel.WebsiteDirectory, directory);
if (!Directory.Exists(directoryPath)) {
return BadRequest(new GenericResponse<IReadOnlyCollection<string>>(false, string.Format(Strings.ErrorIsInvalid, directory)));
return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsInvalid, directory)));
}
string[] files;
@@ -59,7 +62,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
try {
files = Directory.GetFiles(directoryPath);
} catch (Exception e) {
return BadRequest(new GenericResponse<IReadOnlyCollection<string>>(false, string.Format(Strings.ErrorParsingObject, nameof(files)) + Environment.NewLine + e));
return StatusCode((int) HttpStatusCode.InternalServerError, new GenericResponse(false, string.Format(Strings.ErrorParsingObject, nameof(files)) + Environment.NewLine + e));
}
HashSet<string> result = files.Select(Path.GetFileName).ToHashSet();
@@ -74,16 +77,18 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// This is internal API being utilizied by our ASF-ui IPC frontend. You should not depend on existence of any /Api/WWW endpoints as they can disappear and change anytime.
/// </remarks>
[HttpGet("GitHub/Releases")]
[ProducesResponseType(typeof(GenericResponse<IReadOnlyCollection<GitHubReleaseResponse>>), 200)]
public async Task<ActionResult<GenericResponse<IReadOnlyCollection<GitHubReleaseResponse>>>> GitHubReleasesGet([FromQuery] byte count = 10) {
[ProducesResponseType(typeof(GenericResponse<IReadOnlyCollection<GitHubReleaseResponse>>), (int) HttpStatusCode.OK)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.ServiceUnavailable)]
public async Task<ActionResult<GenericResponse>> GitHubReleasesGet([FromQuery] byte count = 10) {
if (count == 0) {
return BadRequest(new GenericResponse<IReadOnlyCollection<GitHubReleaseResponse>>(false, string.Format(Strings.ErrorIsEmpty, nameof(count))));
return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(count))));
}
ImmutableList<GitHub.ReleaseResponse> response = await GitHub.GetReleases(count).ConfigureAwait(false);
if ((response == null) || (response.Count == 0)) {
return BadRequest(new GenericResponse<IReadOnlyCollection<GitHub.ReleaseResponse>>(false, string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries)));
return StatusCode((int) HttpStatusCode.ServiceUnavailable, new GenericResponse(false, string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries)));
}
List<GitHubReleaseResponse> result = response.Select(singleResponse => new GitHubReleaseResponse(singleResponse)).ToList();
@@ -98,19 +103,17 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// This is internal API being utilizied by our ASF-ui IPC frontend. You should not depend on existence of any /Api/WWW endpoints as they can disappear and change anytime.
/// </remarks>
[HttpGet("GitHub/Releases/{version:required}")]
[ProducesResponseType(typeof(GenericResponse<GitHubReleaseResponse>), 200)]
public async Task<ActionResult<GenericResponse<GitHubReleaseResponse>>> GitHubReleasesGet(string version) {
[ProducesResponseType(typeof(GenericResponse<GitHubReleaseResponse>), (int) HttpStatusCode.OK)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.ServiceUnavailable)]
public async Task<ActionResult<GenericResponse>> GitHubReleasesGet(string version) {
if (string.IsNullOrEmpty(version)) {
return BadRequest(new GenericResponse<GitHubReleaseResponse>(false, string.Format(Strings.ErrorIsEmpty, nameof(version))));
return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(version))));
}
GitHub.ReleaseResponse releaseResponse = await GitHub.GetRelease(version).ConfigureAwait(false);
if (releaseResponse == null) {
return BadRequest(new GenericResponse<GitHubReleaseResponse>(false, string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries)));
}
return Ok(new GenericResponse<GitHubReleaseResponse>(new GitHubReleaseResponse(releaseResponse)));
return releaseResponse != null ? Ok(new GenericResponse<GitHubReleaseResponse>(new GitHubReleaseResponse(releaseResponse))) : StatusCode((int) HttpStatusCode.ServiceUnavailable, new GenericResponse(false, string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries)));
}
/// <summary>
@@ -121,25 +124,23 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// </remarks>
[Consumes("application/json")]
[HttpPost("Send")]
[ProducesResponseType(typeof(GenericResponse<string>), 200)]
public async Task<ActionResult<GenericResponse<string>>> SendPost([FromBody] WWWSendRequest request) {
[ProducesResponseType(typeof(GenericResponse<string>), (int) HttpStatusCode.OK)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.ServiceUnavailable)]
public async Task<ActionResult<GenericResponse>> SendPost([FromBody] WWWSendRequest request) {
if (request == null) {
ASF.ArchiLogger.LogNullError(nameof(request));
return BadRequest(new GenericResponse<string>(false, string.Format(Strings.ErrorIsEmpty, nameof(request))));
return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(request))));
}
if (string.IsNullOrEmpty(request.URL) || !Uri.TryCreate(request.URL, UriKind.Absolute, out Uri uri) || !uri.Scheme.Equals(Uri.UriSchemeHttps)) {
return BadRequest(new GenericResponse<string>(false, string.Format(Strings.ErrorIsInvalid, nameof(request.URL))));
return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsInvalid, nameof(request.URL))));
}
WebBrowser.StringResponse urlResponse = await ASF.WebBrowser.UrlGetToString(request.URL).ConfigureAwait(false);
if (urlResponse?.Content == null) {
return BadRequest(new GenericResponse<string>(false, string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries)));
}
return Ok(new GenericResponse<string>(urlResponse.Content));
return urlResponse?.Content != null ? Ok(new GenericResponse<string>(urlResponse.Content)) : StatusCode((int) HttpStatusCode.ServiceUnavailable, new GenericResponse(false, string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries)));
}
}
}