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">
|
<Content Update="Views\Server\P2PService.cshtml">
|
||||||
<Pack>$(IncludeRazorContentInPack)</Pack>
|
<Pack>$(IncludeRazorContentInPack)</Pack>
|
||||||
</Content>
|
</Content>
|
||||||
|
<Content Update="Views\Server\DynamicDnsService.cshtml">
|
||||||
|
<Pack>$(IncludeRazorContentInPack)</Pack>
|
||||||
|
</Content>
|
||||||
<Content Update="Views\Server\SSHService.cshtml">
|
<Content Update="Views\Server\SSHService.cshtml">
|
||||||
<Pack>$(IncludeRazorContentInPack)</Pack>
|
<Pack>$(IncludeRazorContentInPack)</Pack>
|
||||||
</Content>
|
</Content>
|
||||||
|
@ -545,6 +545,11 @@ namespace BTCPayServer.Controllers
|
|||||||
Link = this.Url.Action(nameof(SSHService))
|
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)
|
foreach (var torService in _torServices.Services)
|
||||||
{
|
{
|
||||||
if (torService.VirtualPort == 80)
|
if (torService.VirtualPort == 80)
|
||||||
@ -801,6 +806,39 @@ 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")]
|
||||||
|
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")]
|
[Route("server/services/ssh")]
|
||||||
public IActionResult SSHService(bool downloadKeyFile = false)
|
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, RatesHostedService>();
|
||||||
services.AddSingleton<IHostedService, BackgroundJobSchedulerHostedService>();
|
services.AddSingleton<IHostedService, BackgroundJobSchedulerHostedService>();
|
||||||
services.AddSingleton<IHostedService, AppHubStreamer>();
|
services.AddSingleton<IHostedService, AppHubStreamer>();
|
||||||
|
services.AddSingleton<IHostedService, DynamicDnsHostedService>();
|
||||||
services.AddSingleton<IHostedService, TorServicesHostedService>();
|
services.AddSingleton<IHostedService, TorServicesHostedService>();
|
||||||
services.AddSingleton<IHostedService, PaymentRequestStreamer>();
|
services.AddSingleton<IHostedService, PaymentRequestStreamer>();
|
||||||
services.AddSingleton<IBackgroundJobClient, BackgroundJobClient>();
|
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