From e3b348b55c054303d34987247b505aa7699e9142 Mon Sep 17 00:00:00 2001 From: Dennis Reimann Date: Sat, 16 May 2020 23:57:49 +0200 Subject: [PATCH] GreenField: Add Server Info API basics --- .../BTCPayServerClient.ServerInfo.cs | 16 +++ BTCPayServer.Client/Models/ServerInfoData.cs | 47 +++++++++ BTCPayServer.Tests/GreenfieldAPITests.cs | 22 +++++ BTCPayServer/BTCPayServer.csproj | 4 + .../GreenField/ServerInfoController.cs | 73 ++++++++++++++ .../v1/swagger.template.serverinfo.json | 99 +++++++++++++++++++ 6 files changed, 261 insertions(+) create mode 100644 BTCPayServer.Client/BTCPayServerClient.ServerInfo.cs create mode 100644 BTCPayServer.Client/Models/ServerInfoData.cs create mode 100644 BTCPayServer/Controllers/GreenField/ServerInfoController.cs create mode 100644 BTCPayServer/wwwroot/swagger/v1/swagger.template.serverinfo.json diff --git a/BTCPayServer.Client/BTCPayServerClient.ServerInfo.cs b/BTCPayServer.Client/BTCPayServerClient.ServerInfo.cs new file mode 100644 index 000000000..eab3f4b8f --- /dev/null +++ b/BTCPayServer.Client/BTCPayServerClient.ServerInfo.cs @@ -0,0 +1,16 @@ +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using BTCPayServer.Client.Models; + +namespace BTCPayServer.Client +{ + public partial class BTCPayServerClient + { + public virtual async Task GetServerInfo(CancellationToken token = default) + { + var response = await _httpClient.SendAsync(CreateHttpRequest("api/v1/server/info"), token); + return await HandleResponse(response); + } + } +} diff --git a/BTCPayServer.Client/Models/ServerInfoData.cs b/BTCPayServer.Client/Models/ServerInfoData.cs new file mode 100644 index 000000000..edd301ca3 --- /dev/null +++ b/BTCPayServer.Client/Models/ServerInfoData.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; + +namespace BTCPayServer.Client.Models +{ + public class ServerInfoData + { + /// + /// detailed status information + /// + public ServerInfoStatusData Status { get; set; } + + /// + /// the BTCPay Server version + /// + public string Version { get; set; } + + /// + /// the Tor hostname + /// + public string Onion { get; set; } + + /// + /// the payment methods this server supports + /// + public IEnumerable SupportedPaymentMethods { get; set; } + } + + public class ServerInfoStatusData + { + /// + /// are all chains fully synched + /// + public bool FullySynched { get; set; } + + /// + /// detailed sync information per chain + /// + public IEnumerable SyncStatus { get; set; } + } + + public class ServerInfoSyncStatusData + { + public string CryptoCode { get; set; } + public int BlockHeaders { get; set; } + public double Progress { get; set; } + } +} diff --git a/BTCPayServer.Tests/GreenfieldAPITests.cs b/BTCPayServer.Tests/GreenfieldAPITests.cs index faf0b7729..4b58513e9 100644 --- a/BTCPayServer.Tests/GreenfieldAPITests.cs +++ b/BTCPayServer.Tests/GreenfieldAPITests.cs @@ -291,5 +291,27 @@ namespace BTCPayServer.Tests Assert.True(apiHealthData.Synchronized); } } + + [Fact(Timeout = TestTimeout)] + [Trait("Integration", "Integration")] + public async Task ServerInfoControllerTests() + { + using (var tester = ServerTester.Create()) + { + await tester.StartAsync(); + var unauthClient = new BTCPayServerClient(tester.PayTester.ServerUri); + await AssertHttpError(401, async () => await unauthClient.GetServerInfo()); + + var user = tester.NewAccount(); + user.GrantAccess(); + var clientBasic = await user.CreateClient(); + var serverInfoData = await clientBasic.GetServerInfo(); + Assert.NotNull(serverInfoData); + Assert.NotNull(serverInfoData.Status); + Assert.True(serverInfoData.Status.FullySynched); + Assert.Contains("BTC", serverInfoData.SupportedPaymentMethods); + Assert.Contains("BTC_LightningLike", serverInfoData.SupportedPaymentMethods); + } + } } } diff --git a/BTCPayServer/BTCPayServer.csproj b/BTCPayServer/BTCPayServer.csproj index c37a38ca5..e23f17bcc 100644 --- a/BTCPayServer/BTCPayServer.csproj +++ b/BTCPayServer/BTCPayServer.csproj @@ -21,6 +21,10 @@ + + true + PreserveNewest + diff --git a/BTCPayServer/Controllers/GreenField/ServerInfoController.cs b/BTCPayServer/Controllers/GreenField/ServerInfoController.cs new file mode 100644 index 000000000..255c7a480 --- /dev/null +++ b/BTCPayServer/Controllers/GreenField/ServerInfoController.cs @@ -0,0 +1,73 @@ +using System.Linq; +using System.Threading.Tasks; +using BTCPayServer.Client.Models; +using BTCPayServer.Data; +using BTCPayServer.HostedServices; +using BTCPayServer.Security; +using BTCPayServer.Services; +using BTCPayServer.Services.Invoices; +using BTCPayServer.Services.Stores; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; + +namespace BTCPayServer.Controllers.GreenField +{ + [ApiController] + public class GreenFieldServerInfoController : Controller + { + private readonly BTCPayServerEnvironment _env; + private readonly NBXplorerDashboard _dashBoard; + private readonly StoreRepository _storeRepository; + private readonly UserManager _userManager; + private readonly BTCPayNetworkProvider _networkProvider; + private readonly PaymentMethodHandlerDictionary _paymentMethodHandlerDictionary; + + public GreenFieldServerInfoController( + BTCPayServerEnvironment env, + NBXplorerDashboard dashBoard, + StoreRepository storeRepository, + UserManager userManager, + BTCPayNetworkProvider networkProvider, + PaymentMethodHandlerDictionary paymentMethodHandlerDictionary) + { + _env = env; + _dashBoard = dashBoard; + _storeRepository = storeRepository; + _userManager = userManager; + _networkProvider = networkProvider; + _paymentMethodHandlerDictionary = paymentMethodHandlerDictionary; + } + + [Authorize(AuthenticationSchemes = AuthenticationSchemes.Greenfield)] + [HttpGet("~/api/v1/server/info")] + public async Task ServerInfo() + { + var stores = await _storeRepository.GetStoresByUserId(_userManager.GetUserId(User)); + var supportedPaymentMethods = _paymentMethodHandlerDictionary + .SelectMany(handler => handler.GetSupportedPaymentMethods().Select(id => id.ToString())) + .Distinct(); + var syncStatus = _dashBoard.GetAll() + .Select(summary => new ServerInfoSyncStatusData + { + CryptoCode = summary.Network.CryptoCode, + // TODO: Implement these fields + BlockHeaders = 0, + Progress = 0 + }); + ServerInfoStatusData status = new ServerInfoStatusData + { + FullySynched = _dashBoard.IsFullySynched(), + SyncStatus = syncStatus + }; + ServerInfoData model = new ServerInfoData + { + Status = status, + Onion = _env.OnionUrl, + Version = _env.Version, + SupportedPaymentMethods = supportedPaymentMethods + }; + return Ok(model); + } + } +} diff --git a/BTCPayServer/wwwroot/swagger/v1/swagger.template.serverinfo.json b/BTCPayServer/wwwroot/swagger/v1/swagger.template.serverinfo.json new file mode 100644 index 000000000..c1ef20713 --- /dev/null +++ b/BTCPayServer/wwwroot/swagger/v1/swagger.template.serverinfo.json @@ -0,0 +1,99 @@ +{ + "paths": { + "/api/v1/server/info": { + "get": { + "tags": [ + "ServerInfo" + ], + "summary": "Get server info", + "description": "Information about the server, chains and sync states", + "operationId": "ServerInfo_GetServerInfo", + "responses": { + "200": { + "description": "Server information", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApplicationServerInfoData" + } + } + } + } + }, + "security": [ + { + "API Key": [], + "Basic": [] + } + ] + } + } + }, + "components": { + "schemas": { + "ApplicationServerInfoData": { + "type": "object", + "properties": { + "status": { + "$ref": "#/components/schemas/ApplicationServerInfoStatusData" + }, + "version": { + "type": "string", + "description": "BTCPay Server version" + }, + "onion": { + "type": "string", + "description": "The Tor hostname" + }, + "supportedPaymentMethods": { + "type": "array", + "description": "The payment methods this server supports", + "items": { + "type": "string" + } + } + } + }, + "ApplicationServerInfoStatusData": { + "type": "object", + "description": "Detailed sync status", + "properties": { + "fullySynched": { + "type": "boolean", + "description": "True if the instance is fully synchronized, according to NBXplorer" + }, + "syncStatus": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ApplicationServerInfoSyncStatusData" + } + } + } + }, + "ApplicationServerInfoSyncStatusData": { + "type": "object", + "description": "Detailed sync status", + "properties": { + "cryptoCode": { + "type": "string", + "description": "True if the instance is fully synchronized, according to NBXplorer" + }, + "blockHeaders": { + "type": "integer", + "description": "True if the instance is fully synchronized, according to NBXplorer" + }, + "progress": { + "type": "number", + "format": "double", + "description": "True if the instance is fully synchronized, according to NBXplorer" + } + } + } + } + }, + "tags": [ + { + "name": "ServerInfo" + } + ] +}