Can configure multiple dynamic dns

This commit is contained in:
nicolas.dorier
2019-07-25 18:29:18 +09:00
parent db57b5ae80
commit 63472d54d7
7 changed files with 195 additions and 56 deletions

View File

@@ -146,6 +146,9 @@
<Content Update="Views\Home\BitpayTranslator.cshtml"> <Content Update="Views\Home\BitpayTranslator.cshtml">
<Pack>$(IncludeRazorContentInPack)</Pack> <Pack>$(IncludeRazorContentInPack)</Pack>
</Content> </Content>
<Content Update="Views\Server\DynamicDnsServices.cshtml">
<Pack>$(IncludeRazorContentInPack)</Pack>
</Content>
<Content Update="Views\Server\LightningChargeServices.cshtml"> <Content Update="Views\Server\LightningChargeServices.cshtml">
<Pack>$(IncludeRazorContentInPack)</Pack> <Pack>$(IncludeRazorContentInPack)</Pack>
</Content> </Content>

View File

@@ -548,7 +548,7 @@ namespace BTCPayServer.Controllers
result.OtherExternalServices.Add(new ServicesViewModel.OtherExternalService() result.OtherExternalServices.Add(new ServicesViewModel.OtherExternalService()
{ {
Name = "Dynamic DNS", Name = "Dynamic DNS",
Link = this.Url.Action(nameof(DynamicDnsService)) Link = this.Url.Action(nameof(DynamicDnsServices))
}); });
foreach (var torService in _torServices.Services) foreach (var torService in _torServices.Services)
{ {
@@ -806,37 +806,99 @@ namespace BTCPayServer.Controllers
return RedirectToAction(nameof(Service), new { cryptoCode = cryptoCode, serviceName = serviceName, nonce = nonce }); return RedirectToAction(nameof(Service), new { cryptoCode = cryptoCode, serviceName = serviceName, nonce = nonce });
} }
[Route("server/services/dynamic-dns")] [Route("server/services/dynamic-dns")]
public async Task<IActionResult> DynamicDnsService() public async Task<IActionResult> DynamicDnsServices()
{ {
var settings = (await _SettingsRepository.GetSettingAsync<DynamicDnsSettings>()) ?? new DynamicDnsSettings(); var settings = (await _SettingsRepository.GetSettingAsync<DynamicDnsSettings>()) ?? new DynamicDnsSettings();
return View(settings.Services.Select(s => new DynamicDnsViewModel()
{
Settings = s
}).ToArray());
}
[Route("server/services/dynamic-dns/{hostname}")]
public async Task<IActionResult> DynamicDnsServices(string hostname)
{
var settings = (await _SettingsRepository.GetSettingAsync<DynamicDnsSettings>()) ?? new DynamicDnsSettings();
var service = settings.Services.FirstOrDefault(s => s.Hostname.Equals(hostname, StringComparison.OrdinalIgnoreCase));
if (service == null)
return NotFound();
var vm = new DynamicDnsViewModel(); var vm = new DynamicDnsViewModel();
vm.Settings = settings; vm.Modify = true;
return View(vm); vm.Settings = service;
return View(nameof(DynamicDnsService), vm);
} }
[Route("server/services/dynamic-dns")] [Route("server/services/dynamic-dns")]
[HttpPost] [HttpPost]
public async Task<IActionResult> DynamicDnsService(DynamicDnsViewModel viewModel, string command = null) public async Task<IActionResult> DynamicDnsService(DynamicDnsViewModel viewModel, string command = null)
{ {
if (!ModelState.IsValid)
{
return View(viewModel);
}
if (command == "Save")
{
var settings = (await _SettingsRepository.GetSettingAsync<DynamicDnsSettings>()) ?? new DynamicDnsSettings();
var i = settings.Services.FindIndex(d => d.Hostname.Equals(viewModel.Settings.Hostname, StringComparison.OrdinalIgnoreCase));
if (i != -1)
{
ModelState.AddModelError(nameof(viewModel.Settings.Hostname), "This hostname already exists");
return View(viewModel);
}
string errorMessage = await viewModel.Settings.SendUpdateRequest(HttpClientFactory.CreateClient());
if (errorMessage == null)
{
StatusMessage = $"The Dynamic DNS has been successfully queried, your configuration is saved";
viewModel.Settings.LastUpdated = DateTimeOffset.UtcNow;
settings.Services.Add(viewModel.Settings);
await _SettingsRepository.UpdateSetting(settings);
return RedirectToAction(nameof(DynamicDnsServices));
}
else
{
ModelState.AddModelError(string.Empty, errorMessage);
return View(viewModel);
}
}
else
{
return View(new DynamicDnsViewModel() { Settings = new DynamicDnsService() });
}
}
[Route("server/services/dynamic-dns/{hostname}")]
[HttpPost]
public async Task<IActionResult> DynamicDnsService(DynamicDnsViewModel viewModel, string hostname, string command = null)
{
var settings = (await _SettingsRepository.GetSettingAsync<DynamicDnsSettings>()) ?? new DynamicDnsSettings();
var i = settings.Services.FindIndex(d => d.Hostname.Equals(hostname, StringComparison.OrdinalIgnoreCase));
if (i == -1)
return NotFound();
if (viewModel.Settings.Password == null)
viewModel.Settings.Password = settings.Services[i].Password;
if (!viewModel.Settings.Enabled) if (!viewModel.Settings.Enabled)
{ {
StatusMessage = $"The Dynamic DNS service has been disabled"; StatusMessage = $"The Dynamic DNS service has been disabled";
viewModel.Settings.LastUpdated = null; viewModel.Settings.LastUpdated = null;
await _SettingsRepository.UpdateSetting(viewModel.Settings);
return RedirectToAction();
}
string errorMessage = await viewModel.Settings.SendUpdateRequest(HttpClientFactory.CreateClient());
if (errorMessage == null)
{
StatusMessage = $"The Dynamic DNS has been successfully queried, your configuration is saved";
viewModel.Settings.LastUpdated = DateTimeOffset.UtcNow;
await _SettingsRepository.UpdateSetting(viewModel.Settings);
} }
else else
{ {
StatusMessage = errorMessage; string errorMessage = await viewModel.Settings.SendUpdateRequest(HttpClientFactory.CreateClient());
if (errorMessage == null)
{
StatusMessage = $"The Dynamic DNS has been successfully queried, your configuration is saved";
viewModel.Settings.LastUpdated = DateTimeOffset.UtcNow;
}
else
{
ModelState.AddModelError(string.Empty, errorMessage);
return View(viewModel);
}
} }
return RedirectToAction(); settings.Services[i] = viewModel.Settings;
await _SettingsRepository.UpdateSetting(settings);
this.RouteData.Values.Remove(nameof(hostname));
return RedirectToAction(nameof(DynamicDnsServices));
} }
[Route("server/services/ssh")] [Route("server/services/ssh")]

View File

@@ -35,27 +35,30 @@ namespace BTCPayServer.HostedServices
using (var timeout = CancellationTokenSource.CreateLinkedTokenSource(Cancellation)) using (var timeout = CancellationTokenSource.CreateLinkedTokenSource(Cancellation))
{ {
var settings = await SettingsRepository.GetSettingAsync<DynamicDnsSettings>(); var settings = await SettingsRepository.GetSettingAsync<DynamicDnsSettings>();
if (settings?.Enabled is true && (settings.LastUpdated is null || foreach (var service in settings.Services)
(DateTimeOffset.UtcNow - settings.LastUpdated) > Period))
{ {
timeout.CancelAfter(TimeSpan.FromSeconds(20.0)); if (service?.Enabled is true && (service.LastUpdated is null ||
try (DateTimeOffset.UtcNow - service.LastUpdated) > Period))
{ {
var errorMessage = await settings.SendUpdateRequest(HttpClientFactory.CreateClient()); timeout.CancelAfter(TimeSpan.FromSeconds(20.0));
if (errorMessage == null) try
{ {
Logs.PayServer.LogInformation("Dynamic DNS service successfully refresh the DNS record"); var errorMessage = await service.SendUpdateRequest(HttpClientFactory.CreateClient());
settings.LastUpdated = DateTimeOffset.UtcNow; if (errorMessage == null)
await SettingsRepository.UpdateSetting(settings); {
Logs.PayServer.LogInformation("Dynamic DNS service successfully refresh the DNS record");
service.LastUpdated = DateTimeOffset.UtcNow;
await SettingsRepository.UpdateSetting(settings);
}
else
{
Logs.PayServer.LogWarning($"Dynamic DNS service is enabled but the request to the provider failed: {errorMessage}");
}
} }
else catch (OperationCanceledException) when (timeout.IsCancellationRequested)
{ {
Logs.PayServer.LogWarning($"Dynamic DNS service is enabled but the request to the provider failed: {errorMessage}");
} }
} }
catch (OperationCanceledException) when (timeout.IsCancellationRequested)
{
}
} }
} }
using (var delayCancel = CancellationTokenSource.CreateLinkedTokenSource(Cancellation)) using (var delayCancel = CancellationTokenSource.CreateLinkedTokenSource(Cancellation))

View File

@@ -19,8 +19,8 @@ namespace BTCPayServer.Models.ServerViewModels
public string Name { get; set; } public string Name { get; set; }
public string Url { get; set; } public string Url { get; set; }
} }
public bool Modify { get; set; }
public DynamicDnsSettings Settings { get; set; } public DynamicDnsService Settings { get; set; }
public string LastUpdated public string LastUpdated
{ {
get get

View File

@@ -5,7 +5,6 @@ using System.ComponentModel.DataAnnotations;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Reflection;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using BTCPayServer.Hosting; using BTCPayServer.Hosting;
@@ -15,15 +14,21 @@ using Newtonsoft.Json;
namespace BTCPayServer.Services namespace BTCPayServer.Services
{ {
public class DynamicDnsSettings public class DynamicDnsSettings
{
public List<DynamicDnsService> Services { get; set; } = new List<DynamicDnsService>();
}
public class DynamicDnsService
{ {
[Display(Name = "Url of the Dynamic DNS service you are using")] [Display(Name = "Url of the Dynamic DNS service you are using")]
[Required]
public string ServiceUrl { get; set; } public string ServiceUrl { get; set; }
public string Login { get; set; } public string Login { get; set; }
[DataType(DataType.Password)] [DataType(DataType.Password)]
public string Password { get; set; } public string Password { get; set; }
[Display(Name = "Your dynamic DNS hostname")] [Display(Name = "Your dynamic DNS hostname")]
[Required]
public string Hostname { get; set; } public string Hostname { get; set; }
public bool Enabled { get; set; } public bool Enabled { get; set; } = true;
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))] [JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
public DateTimeOffset? LastUpdated { get; set; } public DateTimeOffset? LastUpdated { get; set; }
@@ -54,7 +59,7 @@ namespace BTCPayServer.Services
HttpRequestMessage webRequest = new HttpRequestMessage(); HttpRequestMessage webRequest = new HttpRequestMessage();
if (!Uri.TryCreate(ServiceUrl, UriKind.Absolute, out var uri) || uri.HostNameType == UriHostNameType.Unknown) if (!Uri.TryCreate(ServiceUrl, UriKind.Absolute, out var uri) || uri.HostNameType == UriHostNameType.Unknown)
{ {
throw new FormatException($"Invalid {ServiceUrl}"); throw new FormatException($"Invalid service url");
} }
var builder = new UriBuilder(uri); var builder = new UriBuilder(uri);

View File

@@ -4,20 +4,21 @@
} }
<h4>Dynamic DNS Settings</h4> <h4>Dynamic DNS Service</h4>
<partial name="_StatusMessage" for="@TempData["TempDataProperty-StatusMessage"]" /> <partial name="_StatusMessage" for="@TempData["TempDataProperty-StatusMessage"]" />
@if (!this.ViewContext.ModelState.IsValid)
{
<div class="row">
<div class="col-md-8">
<div asp-validation-summary="All" class="text-danger"></div>
</div>
</div>
}
<div class="row"> <div class="row">
<div class="col-md-8"> <div class="col-md-8">
<div class="form-group">
<p>
<span>Dynamic DNS service allows you to have a stable DNS name pointing to your server, even if your IP address change regulary. <br />
This is recommended if you are hosting BTCPayServer at home and wish to have a clearnet HTTPS address to access your server.</span>
</p>
<p>Note that you need to properly configure your NAT and BTCPayServer install to get HTTPS certificate.</p>
</div>
<form method="post"> <form method="post">
<div class="form-group"> <div class="form-group">
<input type="hidden" asp-for="Modify" />
<div class="form-group"> <div class="form-group">
<label asp-for="Settings.ServiceUrl"></label> <label asp-for="Settings.ServiceUrl"></label>
<input id="ServiceUrl" asp-for="Settings.ServiceUrl" class="form-control" placeholder="Url" /> <input id="ServiceUrl" asp-for="Settings.ServiceUrl" class="form-control" placeholder="Url" />
@@ -31,14 +32,22 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label asp-for="Settings.Hostname"></label> <label asp-for="Settings.Hostname"></label>
<input asp-for="Settings.Hostname" class="form-control" placeholder="Hostname" /> @if (Model.Modify)
<p class="form-text text-muted"> {
<span>The DNS record has been refreshed: </span> <input asp-for="Settings.Hostname" class="form-control" readonly placeholder="Hostname" />
@if (Model.LastUpdated != null) <p class="form-text text-muted">
{ <span>The DNS record has been refreshed: </span>
<span>@Model.LastUpdated</span> @if (Model.LastUpdated != null)
} {
</p> <span>@Model.LastUpdated</span>
}
</p>
}
else
{
<input asp-for="Settings.Hostname" class="form-control" placeholder="Hostname" />
<span asp-validation-for="Settings.Hostname" class="text-danger"></span>
}
</div> </div>
<div class="form-group"> <div class="form-group">
<label asp-for="Settings.Login"></label> <label asp-for="Settings.Login"></label>
@@ -48,11 +57,13 @@
<label asp-for="Settings.Password"></label> <label asp-for="Settings.Password"></label>
<input asp-for="Settings.Password" class="form-control" placeholder="Password" /> <input asp-for="Settings.Password" class="form-control" placeholder="Password" />
</div> </div>
<div class="form-group"> @if (Model.Modify)
<label asp-for="Settings.Enabled"></label> {
<input asp-for="Settings.Enabled" class="form-check-inline" type="checkbox" /> <div class="form-group">
</div> <label asp-for="Settings.Enabled"></label>
<input asp-for="Settings.Enabled" class="form-check-inline" type="checkbox" />
</div>
}
<button name="command" class="btn btn-primary" type="submit" value="Save">Save</button> <button name="command" class="btn btn-primary" type="submit" value="Save">Save</button>
</div> </div>
</form> </form>

View File

@@ -0,0 +1,55 @@
@model BTCPayServer.Models.ServerViewModels.DynamicDnsViewModel[]
@{
ViewData.SetActivePageAndTitle(ServerNavPages.Services);
}
<h4>Dynamic DNS Settings</h4>
<partial name="_StatusMessage" for="@TempData["TempDataProperty-StatusMessage"]" />
<div class="row">
<div class="col-md-8">
<div class="form-group">
<p>
<span>
Dynamic DNS service allows you to have a stable DNS name pointing to your server, even if your IP address change regulary. <br />
This is recommended if you are hosting BTCPayServer at home and wish to have a clearnet HTTPS address to access your server.
</span>
</p>
<p>Note that you need to properly configure your NAT and BTCPayServer install to get HTTPS certificate.</p>
</div>
<form method="post" asp-action="DynamicDnsService">
<button class="btn btn-primary" type="submit"><span class="fa fa-plus"></span> Add Dynamic DNS</button>
</form>
<table class="table table-sm table-responsive-md">
<thead>
<tr>
<th>Hostname</th>
<th>Last updated</th>
<th style="text-align:center;">Enabled</th>
<th style="text-align:right">Actions</th>
</tr>
</thead>
<tbody>
@foreach (var service in Model)
{
<tr>
<td>@service.Settings.Hostname</td>
<td>@service.LastUpdated</td>
<td style="text-align:center;">
@if(service.Settings.Enabled)
{
<span class="fa fa-check"></span>
}
else
{
<span class="fa fa-times"></span>
}
</td>
<td><a asp-action="DynamicDnsService" asp-route-hostname="@service.Settings.Hostname">Edit</a> <span> - </span> <a asp-action="DynamicDnsServiceDelete" asp-route-hostname="@service.Settings.Hostname">Remove</a></td>
</tr>
}
</tbody>
</table>
</div>
</div>