mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-01-19 05:33:31 +01:00
Add Dynamic DNS support
This commit is contained in:
parent
8e07bf3ffb
commit
8896d89908
@ -155,6 +155,9 @@
|
||||
<Content Update="Views\Server\P2PService.cshtml">
|
||||
<Pack>$(IncludeRazorContentInPack)</Pack>
|
||||
</Content>
|
||||
<Content Update="Views\Server\DynamicDnsService.cshtml">
|
||||
<Pack>$(IncludeRazorContentInPack)</Pack>
|
||||
</Content>
|
||||
<Content Update="Views\Server\SSHService.cshtml">
|
||||
<Pack>$(IncludeRazorContentInPack)</Pack>
|
||||
</Content>
|
||||
|
@ -545,6 +545,11 @@ namespace BTCPayServer.Controllers
|
||||
Link = this.Url.Action(nameof(SSHService))
|
||||
});
|
||||
}
|
||||
result.OtherExternalServices.Add(new ServicesViewModel.OtherExternalService()
|
||||
{
|
||||
Name = "Dynamic DNS",
|
||||
Link = this.Url.Action(nameof(DynamicDnsService))
|
||||
});
|
||||
foreach (var torService in _torServices.Services)
|
||||
{
|
||||
if (torService.VirtualPort == 80)
|
||||
@ -801,6 +806,39 @@ namespace BTCPayServer.Controllers
|
||||
return RedirectToAction(nameof(Service), new { cryptoCode = cryptoCode, serviceName = serviceName, nonce = nonce });
|
||||
}
|
||||
|
||||
[Route("server/services/dynamic-dns")]
|
||||
public async Task<IActionResult> DynamicDnsService()
|
||||
{
|
||||
var settings = (await _SettingsRepository.GetSettingAsync<DynamicDnsSettings>()) ?? new DynamicDnsSettings();
|
||||
var vm = new DynamicDnsViewModel();
|
||||
vm.Settings = settings;
|
||||
return View(vm);
|
||||
}
|
||||
[Route("server/services/dynamic-dns")]
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> DynamicDnsService(DynamicDnsViewModel viewModel, string command = null)
|
||||
{
|
||||
if (!viewModel.Settings.Enabled)
|
||||
{
|
||||
StatusMessage = $"The Dynamic DNS service has been disabled";
|
||||
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
|
||||
{
|
||||
StatusMessage = errorMessage;
|
||||
}
|
||||
return RedirectToAction();
|
||||
}
|
||||
|
||||
[Route("server/services/ssh")]
|
||||
public IActionResult SSHService(bool downloadKeyFile = false)
|
||||
{
|
||||
|
68
BTCPayServer/HostedServices/DynamicDnsHostedService.cs
Normal file
68
BTCPayServer/HostedServices/DynamicDnsHostedService.cs
Normal file
@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Logging;
|
||||
using BTCPayServer.Services;
|
||||
|
||||
namespace BTCPayServer.HostedServices
|
||||
{
|
||||
public class DynamicDnsHostedService : BaseAsyncService
|
||||
{
|
||||
public DynamicDnsHostedService(IHttpClientFactory httpClientFactory, SettingsRepository settingsRepository)
|
||||
{
|
||||
HttpClientFactory = httpClientFactory;
|
||||
SettingsRepository = settingsRepository;
|
||||
}
|
||||
|
||||
public IHttpClientFactory HttpClientFactory { get; }
|
||||
public SettingsRepository SettingsRepository { get; }
|
||||
|
||||
internal override Task[] InitializeTasks()
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
CreateLoopTask(UpdateRecord)
|
||||
};
|
||||
}
|
||||
|
||||
TimeSpan Period = TimeSpan.FromMinutes(60);
|
||||
async Task UpdateRecord()
|
||||
{
|
||||
using (var timeout = CancellationTokenSource.CreateLinkedTokenSource(Cancellation))
|
||||
{
|
||||
var settings = await SettingsRepository.GetSettingAsync<DynamicDnsSettings>();
|
||||
if (settings?.Enabled is true && (settings.LastUpdated is null ||
|
||||
(DateTimeOffset.UtcNow - settings.LastUpdated) > Period))
|
||||
{
|
||||
timeout.CancelAfter(TimeSpan.FromSeconds(20.0));
|
||||
try
|
||||
{
|
||||
var errorMessage = await settings.SendUpdateRequest(HttpClientFactory.CreateClient());
|
||||
if (errorMessage == null)
|
||||
{
|
||||
Logs.PayServer.LogWarning($"Dynamic DNS service is enabled but the request to the provider failed: {errorMessage}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logs.PayServer.LogInformation("Dynamic DNS service successfully refresh the DNS record");
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException) when (timeout.IsCancellationRequested)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
using (var delayCancel = CancellationTokenSource.CreateLinkedTokenSource(Cancellation))
|
||||
{
|
||||
var delay = Task.Delay(Period, delayCancel.Token);
|
||||
var changed = SettingsRepository.WaitSettingsChanged<DynamicDnsSettings>(Cancellation);
|
||||
await Task.WhenAny(delay, changed);
|
||||
delayCancel.Cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -206,6 +206,7 @@ namespace BTCPayServer.Hosting
|
||||
services.AddSingleton<IHostedService, RatesHostedService>();
|
||||
services.AddSingleton<IHostedService, BackgroundJobSchedulerHostedService>();
|
||||
services.AddSingleton<IHostedService, AppHubStreamer>();
|
||||
services.AddSingleton<IHostedService, DynamicDnsHostedService>();
|
||||
services.AddSingleton<IHostedService, TorServicesHostedService>();
|
||||
services.AddSingleton<IHostedService, PaymentRequestStreamer>();
|
||||
services.AddSingleton<IBackgroundJobClient, BackgroundJobClient>();
|
||||
|
43
BTCPayServer/Models/ServerViewModels/DynamicDnsViewModel.cs
Normal file
43
BTCPayServer/Models/ServerViewModels/DynamicDnsViewModel.cs
Normal file
@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Services;
|
||||
|
||||
namespace BTCPayServer.Models.ServerViewModels
|
||||
{
|
||||
public class DynamicDnsViewModel
|
||||
{
|
||||
public class WellKnownService
|
||||
{
|
||||
public WellKnownService(string name, string url)
|
||||
{
|
||||
Name = name;
|
||||
Url = url;
|
||||
}
|
||||
public string Name { get; set; }
|
||||
public string Url { get; set; }
|
||||
}
|
||||
|
||||
public DynamicDnsSettings Settings { get; set; }
|
||||
public string LastUpdated
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Settings?.LastUpdated is DateTimeOffset date)
|
||||
{
|
||||
return Views.ViewsRazor.ToTimeAgo(date);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public WellKnownService[] KnownServices { get; set; } = new []
|
||||
{
|
||||
new WellKnownService("noip", "https://dynupdate.no-ip.com/nic/update"),
|
||||
new WellKnownService("dyndns", "https://members.dyndns.org/v3/update"),
|
||||
new WellKnownService("duckdns", "https://www.duckdns.org/v3/update"),
|
||||
new WellKnownService("google", "https://domains.google.com/nic/update"),
|
||||
};
|
||||
}
|
||||
}
|
84
BTCPayServer/Services/DynamicDnsSettings.cs
Normal file
84
BTCPayServer/Services/DynamicDnsSettings.cs
Normal file
@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Hosting;
|
||||
using NBitcoin.DataEncoders;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BTCPayServer.Services
|
||||
{
|
||||
public class DynamicDnsSettings
|
||||
{
|
||||
[Display(Name = "Url of the Dynamic DNS service you are using")]
|
||||
public string ServiceUrl { get; set; }
|
||||
public string Login { get; set; }
|
||||
[DataType(DataType.Password)]
|
||||
public string Password { get; set; }
|
||||
[Display(Name = "Your dynamic DNS hostname")]
|
||||
public string Hostname { get; set; }
|
||||
public bool Enabled { get; set; }
|
||||
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
|
||||
public DateTimeOffset? LastUpdated { get; set; }
|
||||
|
||||
public async Task<string> SendUpdateRequest(HttpClient httpClient)
|
||||
{
|
||||
string errorMessage = null;
|
||||
try
|
||||
{
|
||||
var result = await httpClient.SendAsync(CreateUpdateRequest());
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
try
|
||||
{
|
||||
errorMessage = await result.Content.ReadAsStringAsync();
|
||||
}
|
||||
catch { }
|
||||
errorMessage = $"Error: Invalid return code {result.StatusCode}, expected 200 ({errorMessage.Trim()})";
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
errorMessage = $"Error: While querying the Dynamic DNS service ({ex.Message})";
|
||||
}
|
||||
return errorMessage;
|
||||
}
|
||||
public HttpRequestMessage CreateUpdateRequest()
|
||||
{
|
||||
HttpRequestMessage webRequest = new HttpRequestMessage();
|
||||
if (!Uri.TryCreate(ServiceUrl, UriKind.Absolute, out var uri) || uri.HostNameType == UriHostNameType.Unknown)
|
||||
{
|
||||
throw new FormatException($"Invalid {ServiceUrl}");
|
||||
}
|
||||
|
||||
var builder = new UriBuilder(uri);
|
||||
if (!string.IsNullOrEmpty(Login))
|
||||
{
|
||||
builder.UserName = Login;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(Password))
|
||||
{
|
||||
builder.Password = Password;
|
||||
}
|
||||
builder.UserName = builder.UserName ?? string.Empty;
|
||||
builder.Password = builder.Password ?? string.Empty;
|
||||
builder.Query = $"hostname={Hostname}";
|
||||
webRequest.Headers.Authorization = new AuthenticationHeaderValue("Basic", Encoders.Base64.EncodeData(new UTF8Encoding(false).GetBytes($"{builder.UserName}:{builder.Password}")));
|
||||
webRequest.Headers.TryAddWithoutValidation("User-Agent", $"BTCPayServer/{GetVersion()} btcpayserver@gmail.com");
|
||||
webRequest.Method = HttpMethod.Get;
|
||||
webRequest.RequestUri = builder.Uri;
|
||||
return webRequest;
|
||||
}
|
||||
|
||||
private string GetVersion()
|
||||
{
|
||||
return typeof(BTCPayServerEnvironment).GetTypeInfo().Assembly.GetCustomAttribute<AssemblyFileVersionAttribute>().Version;
|
||||
}
|
||||
}
|
||||
}
|
60
BTCPayServer/Views/Server/DynamicDnsService.cshtml
Normal file
60
BTCPayServer/Views/Server/DynamicDnsService.cshtml
Normal file
@ -0,0 +1,60 @@
|
||||
@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">
|
||||
<div class="form-group">
|
||||
<div class="form-group">
|
||||
<label asp-for="Settings.ServiceUrl"></label>
|
||||
<input id="ServiceUrl" asp-for="Settings.ServiceUrl" class="form-control" placeholder="Url" />
|
||||
<p class="form-text text-muted">
|
||||
Well-known Dynamic DNS providers are:
|
||||
@for (int i = 0; i < Model.KnownServices.Length; i++)
|
||||
{
|
||||
<a href="#" onclick="document.getElementById('ServiceUrl').value = '@Model.KnownServices[i].Url'; return false;">@Model.KnownServices[i].Name</a><span>@(i == Model.KnownServices.Length - 1 ? "" : ",")</span>
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Settings.Hostname"></label>
|
||||
<input asp-for="Settings.Hostname" class="form-control" placeholder="Hostname" />
|
||||
<p class="form-text text-muted">
|
||||
<span>The DNS record has been refreshed: </span>
|
||||
@if (Model.LastUpdated != null)
|
||||
{
|
||||
<span>@Model.LastUpdated</span>
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Settings.Login"></label>
|
||||
<input asp-for="Settings.Login" class="form-control" placeholder="Login" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Settings.Password"></label>
|
||||
<input asp-for="Settings.Password" class="form-control" placeholder="Password" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<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>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
Loading…
Reference in New Issue
Block a user