init repo!
104
.circleci/config.yml
Normal file
@ -0,0 +1,104 @@
|
||||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
machine:
|
||||
docker_layer_caching: false
|
||||
steps:
|
||||
- checkout
|
||||
# publish jobs require $DOCKERHUB_REPO, $DOCKERHUB_USER, $DOCKERHUB_PASS defined
|
||||
publish_docker_linuxamd64:
|
||||
machine:
|
||||
docker_layer_caching: false
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
command: |
|
||||
LATEST_TAG=${CIRCLE_TAG:1} #trim v from tag
|
||||
#
|
||||
sudo docker build --pull -t $DOCKERHUB_REPO:$LATEST_TAG-amd64 -f Dockerfiles/Dockerfile.linuxamd64 .
|
||||
sudo docker login --username=$DOCKERHUB_USER --password=$DOCKERHUB_PASS
|
||||
sudo docker push $DOCKERHUB_REPO:$LATEST_TAG-amd64
|
||||
|
||||
publish_docker_linuxarm32:
|
||||
machine:
|
||||
docker_layer_caching: false
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
command: |
|
||||
sudo docker run --rm --privileged multiarch/qemu-user-static:register --reset
|
||||
LATEST_TAG=${CIRCLE_TAG:1} #trim v from tag
|
||||
#
|
||||
sudo docker build --pull -t $DOCKERHUB_REPO:$LATEST_TAG-arm32v7 -f Dockerfiles/Dockerfile.linuxarm32v7 .
|
||||
sudo docker login --username=$DOCKERHUB_USER --password=$DOCKERHUB_PASS
|
||||
sudo docker push $DOCKERHUB_REPO:$LATEST_TAG-arm32v7
|
||||
publish_docker_linuxarm64:
|
||||
machine:
|
||||
docker_layer_caching: false
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
command: |
|
||||
sudo docker run --rm --privileged multiarch/qemu-user-static:register --reset
|
||||
LATEST_TAG=${CIRCLE_TAG:1} #trim v from tag
|
||||
#
|
||||
sudo docker build --pull -t $DOCKERHUB_REPO:$LATEST_TAG-arm64v8 -f Dockerfiles/Dockerfile.linuxarm64v8 .
|
||||
sudo docker login --username=$DOCKERHUB_USER --password=$DOCKERHUB_PASS
|
||||
sudo docker push $DOCKERHUB_REPO:$LATEST_TAG-arm64v8
|
||||
publish_docker_multiarch:
|
||||
machine:
|
||||
enabled: true
|
||||
image: circleci/classic:201808-01
|
||||
steps:
|
||||
- run:
|
||||
command: |
|
||||
# Turn on Experimental features
|
||||
sudo mkdir $HOME/.docker
|
||||
sudo sh -c 'echo "{ \"experimental\": \"enabled\" }" >> $HOME/.docker/config.json'
|
||||
#
|
||||
sudo docker login --username=$DOCKERHUB_USER --password=$DOCKERHUB_PASS
|
||||
#
|
||||
LATEST_TAG=${CIRCLE_TAG:1} #trim v from tag
|
||||
sudo docker manifest create --amend $DOCKERHUB_REPO:$LATEST_TAG $DOCKERHUB_REPO:$LATEST_TAG-amd64 $DOCKERHUB_REPO:$LATEST_TAG-arm32v7
|
||||
sudo docker manifest annotate $DOCKERHUB_REPO:$LATEST_TAG $DOCKERHUB_REPO:$LATEST_TAG-amd64 --os linux --arch amd64
|
||||
sudo docker manifest annotate $DOCKERHUB_REPO:$LATEST_TAG $DOCKERHUB_REPO:$LATEST_TAG-arm32v7 --os linux --arch arm --variant v7
|
||||
sudo docker manifest push $DOCKERHUB_REPO:$LATEST_TAG -p
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
build_and_test:
|
||||
jobs:
|
||||
- test
|
||||
|
||||
publish:
|
||||
jobs:
|
||||
- publish_docker_linuxamd64:
|
||||
filters:
|
||||
# ignore any commit on any branch by default
|
||||
branches:
|
||||
ignore: /.*/
|
||||
# only act on version tags
|
||||
tags:
|
||||
only: /v[0-9]+(\.[0-9]+)*/
|
||||
- publish_docker_linuxarm32:
|
||||
filters:
|
||||
branches:
|
||||
ignore: /.*/
|
||||
tags:
|
||||
only: /v[0-9]+(\.[0-9]+)*/
|
||||
- publish_docker_linuxarm64:
|
||||
filters:
|
||||
branches:
|
||||
ignore: /.*/
|
||||
tags:
|
||||
only: /v[0-9]+(\.[0-9]+)*/
|
||||
- publish_docker_multiarch:
|
||||
requires:
|
||||
- publish_docker_linuxamd64
|
||||
- publish_docker_linuxarm32
|
||||
- publish_docker_linuxarm64
|
||||
filters:
|
||||
branches:
|
||||
ignore: /.*/
|
||||
tags:
|
||||
only: /v[0-9]+(\.[0-9]+)*/
|
||||
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
bin/
|
||||
obj/
|
||||
/packages/
|
||||
.idea
|
||||
16
BTCPayServerDockerConfigurator.sln
Normal file
@ -0,0 +1,16 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BTCPayServerDockerConfigurator", "BTCPayServerDockerConfigurator\BTCPayServerDockerConfigurator.csproj", "{3A44286B-FFC4-4A97-AAB8-6DBFB9269A4A}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{3A44286B-FFC4-4A97-AAB8-6DBFB9269A4A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3A44286B-FFC4-4A97-AAB8-6DBFB9269A4A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3A44286B-FFC4-4A97-AAB8-6DBFB9269A4A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3A44286B-FFC4-4A97-AAB8-6DBFB9269A4A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="3.0.0" />
|
||||
<PackageReference Include="Neon.SSH.NET" Version="0.7.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServerDockerConfigurator.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace BTCPayServerDockerConfigurator.Controllers
|
||||
{
|
||||
public partial class ConfiguratorController
|
||||
{
|
||||
[HttpGet("additional")]
|
||||
public IActionResult AdditionalServices()
|
||||
{
|
||||
var model = GetConfiguratorSettings();
|
||||
|
||||
return View(new UpdateSettings<AdditionalServices, AdditionalDataStub>()
|
||||
{
|
||||
Json = model.ToString(),
|
||||
Settings = model.AdditionalServices
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost("additional")]
|
||||
public async Task<IActionResult> AdditionalServices(
|
||||
UpdateSettings<AdditionalServices, AdditionalDataStub> updateSettings)
|
||||
{
|
||||
var configuratorSettings = string.IsNullOrEmpty(updateSettings.Json)
|
||||
? new ConfiguratorSettings()
|
||||
: JsonSerializer.Deserialize<ConfiguratorSettings>(updateSettings.Json);
|
||||
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
if (updateSettings.Settings.BTCTransmuterSettings.Enabled)
|
||||
{
|
||||
var error = await CheckHost(updateSettings.Settings.BTCTransmuterSettings.Host,
|
||||
configuratorSettings);
|
||||
if (!string.IsNullOrEmpty(error))
|
||||
{
|
||||
ModelState.AddModelError(
|
||||
nameof(updateSettings.Settings) + "." +
|
||||
nameof(updateSettings.Settings.BTCTransmuterSettings) + "." +
|
||||
nameof(updateSettings.Settings.BTCTransmuterSettings.Host),
|
||||
error);
|
||||
}
|
||||
}
|
||||
|
||||
if (updateSettings.Settings.LibrePatronSettings.Enabled)
|
||||
{
|
||||
var error = await CheckHost(updateSettings.Settings.LibrePatronSettings.Host,
|
||||
configuratorSettings);
|
||||
if (!string.IsNullOrEmpty(error))
|
||||
{
|
||||
ModelState.AddModelError(
|
||||
nameof(updateSettings.Settings) + "." +
|
||||
nameof(updateSettings.Settings.LibrePatronSettings) + "." +
|
||||
nameof(updateSettings.Settings.LibrePatronSettings.Host),
|
||||
error);
|
||||
}
|
||||
}
|
||||
|
||||
if (updateSettings.Settings.WooCommerceSettings.Enabled)
|
||||
{
|
||||
var error = await CheckHost(updateSettings.Settings.WooCommerceSettings.Host,
|
||||
configuratorSettings);
|
||||
if (!string.IsNullOrEmpty(error))
|
||||
{
|
||||
ModelState.AddModelError(
|
||||
nameof(updateSettings.Settings) + "." +
|
||||
nameof(updateSettings.Settings.WooCommerceSettings) + "." +
|
||||
nameof(updateSettings.Settings.WooCommerceSettings.Host),
|
||||
error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(updateSettings);
|
||||
}
|
||||
|
||||
configuratorSettings.AdditionalServices = updateSettings.Settings;
|
||||
SetConfiguratorSettings(configuratorSettings);
|
||||
return RedirectToAction(nameof(AdvancedSettings));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,95 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServerDockerConfigurator.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace BTCPayServerDockerConfigurator.Controllers
|
||||
{
|
||||
public partial class ConfiguratorController
|
||||
{
|
||||
[HttpGet("advanced")]
|
||||
public IActionResult AdvancedSettings()
|
||||
{
|
||||
var model = GetConfiguratorSettings();
|
||||
return View(new UpdateSettings<AdvancedSettings, AdvancedSettingsAdditionalData>()
|
||||
{
|
||||
Json = model.ToString(),
|
||||
Settings = model.AdvancedSettings,
|
||||
Additional = new AdvancedSettingsAdditionalData()
|
||||
{
|
||||
ShowSettings = model.AdvancedSettings.AnythingSet()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost("advanced")]
|
||||
public async Task<IActionResult> AdvancedSettings(UpdateSettings<AdvancedSettings, AdvancedSettingsAdditionalData> updateSettings,
|
||||
string command = null)
|
||||
{
|
||||
switch (command)
|
||||
{
|
||||
case "show":
|
||||
updateSettings.Additional = new AdvancedSettingsAdditionalData()
|
||||
{
|
||||
ShowSettings = true
|
||||
};
|
||||
return View(updateSettings);
|
||||
case "add-additional":
|
||||
updateSettings.Additional = new AdvancedSettingsAdditionalData()
|
||||
{
|
||||
ShowSettings = true
|
||||
};
|
||||
updateSettings.Settings.AdditionalFragments.Add("");
|
||||
return View(updateSettings);
|
||||
case string commandx
|
||||
when commandx.StartsWith("remove-additional", StringComparison.InvariantCultureIgnoreCase):
|
||||
{
|
||||
updateSettings.Additional = new AdvancedSettingsAdditionalData()
|
||||
{
|
||||
ShowSettings = true
|
||||
};
|
||||
var index = int.Parse(commandx.Substring(commandx.IndexOf(":", StringComparison.Ordinal) + 1));
|
||||
updateSettings.Settings.AdditionalFragments.RemoveAt(index);
|
||||
return View(updateSettings);
|
||||
}
|
||||
case "add-excluded":
|
||||
updateSettings.Additional = new AdvancedSettingsAdditionalData()
|
||||
{
|
||||
ShowSettings = true
|
||||
};
|
||||
updateSettings.Settings.ExcludedFragments.Add("");
|
||||
return View(updateSettings);
|
||||
case string commandx
|
||||
when commandx.StartsWith("remove-excluded", StringComparison.InvariantCultureIgnoreCase):
|
||||
{
|
||||
updateSettings.Additional = new AdvancedSettingsAdditionalData()
|
||||
{
|
||||
ShowSettings = true
|
||||
};
|
||||
var index = int.Parse(commandx.Substring(commandx.IndexOf(":", StringComparison.Ordinal) + 1));
|
||||
updateSettings.Settings.ExcludedFragments.RemoveAt(index);
|
||||
return View(updateSettings);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
updateSettings.Additional = new AdvancedSettingsAdditionalData()
|
||||
{
|
||||
ShowSettings = true
|
||||
};
|
||||
return View(updateSettings);
|
||||
}
|
||||
|
||||
var configuratorSettings = string.IsNullOrEmpty(updateSettings.Json)
|
||||
? new ConfiguratorSettings()
|
||||
: JsonSerializer.Deserialize<ConfiguratorSettings>(updateSettings.Json);
|
||||
configuratorSettings.AdvancedSettings = updateSettings.Settings;
|
||||
SetConfiguratorSettings(configuratorSettings);
|
||||
return RedirectToAction(nameof(Summary));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServerDockerConfigurator.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace BTCPayServerDockerConfigurator.Controllers
|
||||
{
|
||||
public partial class ConfiguratorController
|
||||
{
|
||||
[HttpGet("chain")]
|
||||
public IActionResult ChainSettings()
|
||||
{
|
||||
var model = GetConfiguratorSettings();
|
||||
return View(new UpdateSettings<ChainSettings, AdditionalDataStub>()
|
||||
{
|
||||
Json = model.ToString(),
|
||||
Settings = model.ChainSettings,
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost("chain")]
|
||||
public async Task<IActionResult> ChainSettings(UpdateSettings<ChainSettings, AdditionalDataStub> updateSettings,
|
||||
string command = null)
|
||||
{
|
||||
switch (command)
|
||||
{
|
||||
case "add-chain":
|
||||
updateSettings.Settings.AltChains.Add("");
|
||||
return View(updateSettings);
|
||||
case string commandx
|
||||
when commandx.StartsWith("remove-chain", StringComparison.InvariantCultureIgnoreCase):
|
||||
{
|
||||
var index = int.Parse(commandx.Substring(commandx.IndexOf(":", StringComparison.Ordinal) + 1));
|
||||
updateSettings.Settings.AltChains.RemoveAt(index);
|
||||
return View(updateSettings);
|
||||
}
|
||||
}
|
||||
|
||||
updateSettings.Settings.AltChains =
|
||||
updateSettings.Settings.AltChains.Where(s => !string.IsNullOrEmpty(s)).ToList();
|
||||
|
||||
if (!updateSettings.Settings.Bitcoin && !updateSettings.Settings.AltChains.Any())
|
||||
{
|
||||
ModelState.AddModelError(
|
||||
nameof(updateSettings.Settings) + "." + nameof(updateSettings.Settings.Bitcoin),
|
||||
"You need to set up at least one chain");
|
||||
}
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(updateSettings);
|
||||
}
|
||||
|
||||
var configuratorSettings = string.IsNullOrEmpty(updateSettings.Json)
|
||||
? new ConfiguratorSettings()
|
||||
: JsonSerializer.Deserialize<ConfiguratorSettings>(updateSettings.Json);
|
||||
configuratorSettings.ChainSettings = updateSettings.Settings;
|
||||
SetConfiguratorSettings(configuratorSettings);
|
||||
return RedirectToAction(nameof(LightningSettings));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServerDockerConfigurator.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace BTCPayServerDockerConfigurator.Controllers
|
||||
{
|
||||
public partial class ConfiguratorController
|
||||
{
|
||||
[HttpPost("deploy")]
|
||||
public async Task<IActionResult> Deploy()
|
||||
{
|
||||
var model = GetConfiguratorSettings();
|
||||
var bash = model.ConstructBashFile(null);
|
||||
var oneliner = bash
|
||||
.Replace("\r\n", "\n")
|
||||
.Replace("\n", " &&\n");
|
||||
|
||||
if (model.DeploymentSettings.DeploymentType == DeploymentType.Manual)
|
||||
{
|
||||
return View(new UpdateSettings<ConfiguratorSettings, DeployAdditionalData>()
|
||||
{
|
||||
Additional = new DeployAdditionalData()
|
||||
{
|
||||
Bash = oneliner
|
||||
},
|
||||
Json = model.ToString(),
|
||||
Settings = model
|
||||
});
|
||||
}
|
||||
SSHSettings ssh = null;;
|
||||
switch (model.DeploymentSettings.DeploymentType)
|
||||
{
|
||||
case DeploymentType.RemoteMachine when ModelState.IsValid:
|
||||
{
|
||||
ssh = new SSHSettings()
|
||||
{
|
||||
Password = model.DeploymentSettings.Password,
|
||||
Server = model.DeploymentSettings.Host,
|
||||
Username = model.DeploymentSettings.Username
|
||||
};
|
||||
break;
|
||||
}
|
||||
case DeploymentType.ThisMachine when ModelState.IsValid:
|
||||
{
|
||||
ssh = _options.Value.ParseSSHConfiguration();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ssh == null)
|
||||
{
|
||||
throw new Exception("lolita bonita");
|
||||
}
|
||||
|
||||
var connection = await ssh.ConnectAsync();
|
||||
var result = await connection.RunBash(oneliner);
|
||||
return View(new UpdateSettings<ConfiguratorSettings, DeployAdditionalData>()
|
||||
{
|
||||
Additional = new DeployAdditionalData()
|
||||
{
|
||||
Bash = oneliner,
|
||||
Error = result.Error,
|
||||
Output = result.Output,
|
||||
ExitStatus = result.ExitStatus
|
||||
},
|
||||
Json = model.ToString(),
|
||||
Settings = model
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public class DeployAdditionalData: SSHClientExtensions.SSHCommandResult
|
||||
{
|
||||
public string Bash { get; set; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,112 @@
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServerDockerConfigurator.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace BTCPayServerDockerConfigurator.Controllers
|
||||
{
|
||||
public partial class ConfiguratorController
|
||||
{
|
||||
[HttpGet("destination")]
|
||||
[HttpGet("")]
|
||||
public IActionResult DeploymentDestination()
|
||||
{
|
||||
var model = GetConfiguratorSettings();
|
||||
|
||||
|
||||
return View(new UpdateSettings<DeploymentSettings, DeploymentAdditionalData>()
|
||||
{
|
||||
Json = JsonSerializer.Serialize(model),
|
||||
Settings = model.DeploymentSettings,
|
||||
Additional = ConstructDeploymentAdditionalData()
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost("destination")]
|
||||
public async Task<IActionResult> DeploymentDestination(
|
||||
UpdateSettings<DeploymentSettings, DeploymentAdditionalData> updateSettings)
|
||||
{
|
||||
updateSettings.Additional = ConstructDeploymentAdditionalData();
|
||||
switch (updateSettings.Settings.DeploymentType)
|
||||
{
|
||||
case DeploymentType.RemoteMachine when ModelState.IsValid:
|
||||
{
|
||||
var ssh = new SSHSettings()
|
||||
{
|
||||
Password = updateSettings.Settings.Password,
|
||||
Server = updateSettings.Settings.Host,
|
||||
Username = updateSettings.Settings.Username
|
||||
};
|
||||
|
||||
if (!await TestSSH(ssh))
|
||||
{
|
||||
ModelState.AddModelError(
|
||||
nameof(updateSettings.Settings) + "." + nameof(updateSettings.Settings.Host),
|
||||
"Could not connect with specified SSH details");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case DeploymentType.ThisMachine when ModelState.IsValid:
|
||||
{
|
||||
var ssh = _options.Value.ParseSSHConfiguration();
|
||||
if (!await TestSSH(ssh))
|
||||
{
|
||||
ModelState.AddModelError(
|
||||
nameof(updateSettings.Settings) + "." + nameof(updateSettings.Settings.DeploymentType),
|
||||
"Couldn't SSH into the host. That's bad fyi.'");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(updateSettings);
|
||||
}
|
||||
|
||||
var configuratorSettings = string.IsNullOrEmpty(updateSettings.Json)
|
||||
? new ConfiguratorSettings()
|
||||
: JsonSerializer.Deserialize<ConfiguratorSettings>(updateSettings.Json);
|
||||
configuratorSettings.DeploymentSettings = updateSettings.Settings;
|
||||
SetConfiguratorSettings(configuratorSettings);
|
||||
return RedirectToAction(nameof(DomainSettings));
|
||||
}
|
||||
|
||||
private async Task<bool> TestSSH(SSHSettings ssh)
|
||||
{
|
||||
try
|
||||
{
|
||||
var test = await ssh.ConnectAsync();
|
||||
if (test.IsConnected)
|
||||
{
|
||||
await test.DisconnectAsync();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private DeploymentAdditionalData ConstructDeploymentAdditionalData()
|
||||
{
|
||||
var additionalData = new DeploymentAdditionalData();
|
||||
if (!string.IsNullOrEmpty(_options.Value.SSHConnection))
|
||||
{
|
||||
additionalData.AvailableDeploymentTypes.Add(DeploymentType.ThisMachine);
|
||||
}
|
||||
|
||||
additionalData.AvailableDeploymentTypes.Add(DeploymentType.RemoteMachine);
|
||||
additionalData.AvailableDeploymentTypes.Add(DeploymentType.Manual);
|
||||
return additionalData;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,90 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServerDockerConfigurator.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace BTCPayServerDockerConfigurator.Controllers
|
||||
{
|
||||
public partial class ConfiguratorController
|
||||
{
|
||||
[HttpGet("domain")]
|
||||
public IActionResult DomainSettings()
|
||||
{
|
||||
var model = GetConfiguratorSettings() ?? new ConfiguratorSettings();
|
||||
|
||||
return View(new UpdateSettings<DomainSettings, AdditionalDataStub>()
|
||||
{
|
||||
Json = JsonSerializer.Serialize(model),
|
||||
Settings = model.DomainSettings,
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost("domain")]
|
||||
public async Task<IActionResult> DomainSettings(
|
||||
UpdateSettings<DomainSettings, AdditionalDataStub> updateSettings,
|
||||
string command = null)
|
||||
{
|
||||
switch (command)
|
||||
{
|
||||
case "add-domain":
|
||||
if (!string.IsNullOrEmpty(updateSettings.Settings.Domain))
|
||||
{
|
||||
updateSettings.Settings.AdditionalDomains.Add("");
|
||||
}
|
||||
|
||||
return View(updateSettings);
|
||||
case string commandx
|
||||
when commandx.StartsWith("remove-domain", StringComparison.InvariantCultureIgnoreCase):
|
||||
{
|
||||
var index = int.Parse(commandx.Substring(commandx.IndexOf(":", StringComparison.Ordinal) + 1));
|
||||
updateSettings.Settings.AdditionalDomains.RemoveAt(index);
|
||||
return View(updateSettings);
|
||||
}
|
||||
}
|
||||
|
||||
if (updateSettings.Settings.AdditionalDomains.Any() &&
|
||||
string.IsNullOrEmpty(updateSettings.Settings.Domain))
|
||||
{
|
||||
ModelState.AddModelError(nameof(updateSettings.Settings) + "." + nameof(updateSettings.Settings.Domain),
|
||||
"You cannot set additional domains when there is no primary domain set!");
|
||||
}
|
||||
|
||||
var configuratorSettings = string.IsNullOrEmpty(updateSettings.Json)
|
||||
? new ConfiguratorSettings()
|
||||
: JsonSerializer.Deserialize<ConfiguratorSettings>(updateSettings.Json);
|
||||
|
||||
var error = await CheckHost(updateSettings.Settings.Domain, configuratorSettings);
|
||||
if (!string.IsNullOrEmpty(error))
|
||||
{
|
||||
ModelState.AddModelError(nameof(updateSettings.Settings) + "." + nameof(updateSettings.Settings.Domain),
|
||||
error);
|
||||
}
|
||||
|
||||
error = null;
|
||||
for (var index = 0; index < updateSettings.Settings.AdditionalDomains.Count; index++)
|
||||
{
|
||||
var additionalDomain = updateSettings.Settings.AdditionalDomains[index];
|
||||
error = await CheckHost(additionalDomain, configuratorSettings);
|
||||
if (!string.IsNullOrEmpty(error))
|
||||
{
|
||||
ModelState.AddModelError(
|
||||
nameof(updateSettings.Settings) + "." + nameof(updateSettings.Settings.Domain) + $"[{index}]",
|
||||
error);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(updateSettings);
|
||||
}
|
||||
|
||||
configuratorSettings.DomainSettings = updateSettings.Settings;
|
||||
SetConfiguratorSettings(configuratorSettings);
|
||||
return RedirectToAction(nameof(ChainSettings));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServerDockerConfigurator.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace BTCPayServerDockerConfigurator.Controllers
|
||||
{
|
||||
public partial class ConfiguratorController
|
||||
{
|
||||
[HttpGet("lightning")]
|
||||
public IActionResult LightningSettings()
|
||||
{
|
||||
var model = GetConfiguratorSettings();
|
||||
|
||||
return View(new UpdateSettings<LightningSettings, AdditionalDataStub>()
|
||||
{
|
||||
Json = model.ToString(),
|
||||
Settings = model.LightningSettings,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
[HttpPost("lightning")]
|
||||
public async Task<IActionResult> LightningSettings(
|
||||
UpdateSettings<LightningSettings, AdditionalDataStub> updateSettings)
|
||||
{
|
||||
var configuratorSettings = string.IsNullOrEmpty(updateSettings.Json)
|
||||
? new ConfiguratorSettings()
|
||||
: JsonSerializer.Deserialize<ConfiguratorSettings>(updateSettings.Json);
|
||||
if (configuratorSettings.ChainSettings.PruneMode != PruneMode.NoPruning &&
|
||||
updateSettings.Settings.Implementation == "eclair")
|
||||
{
|
||||
ModelState.AddModelError(
|
||||
nameof(updateSettings.Settings) + "." + nameof(updateSettings.Settings.Implementation),
|
||||
"You cannot use Eclair when you have pruning enabled.");
|
||||
}else if (configuratorSettings.ChainSettings.PruneMode == PruneMode.ExtraExtraSmall &&
|
||||
!string.IsNullOrEmpty(updateSettings.Settings.Implementation))
|
||||
{
|
||||
ModelState.AddModelError(
|
||||
nameof(updateSettings.Settings) + "." + nameof(updateSettings.Settings.Implementation),
|
||||
"You cannot use lightning when you have your level of pruning set.");
|
||||
}
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(updateSettings);
|
||||
}
|
||||
|
||||
configuratorSettings.LightningSettings = updateSettings.Settings;
|
||||
SetConfiguratorSettings(configuratorSettings);
|
||||
return RedirectToAction(nameof(AdditionalServices));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
using BTCPayServerDockerConfigurator.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace BTCPayServerDockerConfigurator.Controllers
|
||||
{
|
||||
public partial class ConfiguratorController
|
||||
{
|
||||
[HttpGet("summary")]
|
||||
public IActionResult Summary()
|
||||
{
|
||||
var model = GetConfiguratorSettings();
|
||||
return View(new UpdateSettings<ConfiguratorSettings, AdditionalDataStub>()
|
||||
{
|
||||
|
||||
Settings = model
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using BTCPayServerDockerConfigurator.Models;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Renci.SshNet;
|
||||
using Options = BTCPayServerDockerConfigurator.Models.Options;
|
||||
|
||||
namespace BTCPayServerDockerConfigurator.Controllers
|
||||
{
|
||||
[Route("")]
|
||||
public partial class ConfiguratorController : Controller
|
||||
{
|
||||
private readonly IOptions<Options> _options;
|
||||
|
||||
public ConfiguratorController(IOptions<Options> options)
|
||||
{
|
||||
_options = options;
|
||||
}
|
||||
private ConfiguratorSettings GetConfiguratorSettings()
|
||||
{
|
||||
var rawResult = TempData.Peek(nameof(ConfiguratorSettings))?.ToString();
|
||||
return string.IsNullOrEmpty(rawResult)
|
||||
? new ConfiguratorSettings()
|
||||
: JsonSerializer.Deserialize<ConfiguratorSettings>(rawResult);
|
||||
}
|
||||
|
||||
private void SetConfiguratorSettings(ConfiguratorSettings settings)
|
||||
{
|
||||
if (TempData.ContainsKey(nameof(ConfiguratorSettings)))
|
||||
TempData.Remove(nameof(ConfiguratorSettings));
|
||||
TempData.Add(nameof(ConfiguratorSettings), JsonSerializer.Serialize(settings));
|
||||
}
|
||||
|
||||
private async Task<string> CheckHost(string host, ConfiguratorSettings configuratorSettings)
|
||||
{
|
||||
string hostToCheckAgainst = null;
|
||||
switch (configuratorSettings.DeploymentSettings.DeploymentType)
|
||||
{
|
||||
case DeploymentType.Manual:
|
||||
break;
|
||||
case DeploymentType.ThisMachine:
|
||||
hostToCheckAgainst = new WebClient().DownloadString("http://icanhazip.com");
|
||||
break;
|
||||
case DeploymentType.RemoteMachine:
|
||||
hostToCheckAgainst = configuratorSettings.DeploymentSettings.Host;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
var errorMessage = string.IsNullOrEmpty(hostToCheckAgainst)
|
||||
? "The domain was invalid."
|
||||
: $"The domain was either invalid or the DNS record did not seem to point to {hostToCheckAgainst}";
|
||||
|
||||
if (!string.IsNullOrEmpty(host))
|
||||
{
|
||||
if (!await CheckHostDNSIsCorrect(host, hostToCheckAgainst))
|
||||
{
|
||||
return errorMessage;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task<bool> CheckHostDNSIsCorrect(string host, string hostToCheckAgainst = null)
|
||||
{
|
||||
var basicCheck = Uri.CheckHostName(host);
|
||||
if (basicCheck != UriHostNameType.Dns && basicCheck != UriHostNameType.Unknown)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hostToCheckAgainst == null || host.EndsWith(".local", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var vpsType = Uri.CheckHostName(hostToCheckAgainst);
|
||||
if (vpsType == UriHostNameType.Dns)
|
||||
{
|
||||
var vpsIps = Dns.GetHostAddresses(hostToCheckAgainst);
|
||||
return Dns.GetHostAddresses(host).ToList().Any(address =>
|
||||
vpsIps.Any(ipAddress =>
|
||||
ipAddress.Equals(address)));
|
||||
}
|
||||
|
||||
return Dns.GetHostAddresses(host).ToList().Any(address =>
|
||||
address.ToString().Equals(hostToCheckAgainst, StringComparison.InvariantCultureIgnoreCase));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
namespace BTCPayServerDockerConfigurator.Models
|
||||
{
|
||||
public class AdditionalDataStub
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
10
BTCPayServerDockerConfigurator/Models/AdditionalServices.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace BTCPayServerDockerConfigurator.Models
|
||||
{
|
||||
public class AdditionalServices
|
||||
{
|
||||
public LibrePatronSettings LibrePatronSettings { get; set; }
|
||||
public BTCTransmuterSettings BTCTransmuterSettings { get; set; }
|
||||
public WooCommerceSettings WooCommerceSettings { get; set; }
|
||||
public TorRelaySettings TorRelaySettings { get; set; }
|
||||
}
|
||||
}
|
||||
24
BTCPayServerDockerConfigurator/Models/AdvancedSettings.cs
Normal file
@ -0,0 +1,24 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
|
||||
namespace BTCPayServerDockerConfigurator.Models
|
||||
{
|
||||
public class AdvancedSettings
|
||||
{
|
||||
[Display(Name = "Custom BTCPay Docker image")]
|
||||
public string CustomBTCPayImage { get; set; } = "";
|
||||
[Display(Name = "Custom btcpayserver-docker repository")]
|
||||
public string BTCPayDockerRepository { get; set; }
|
||||
[Display(Name = "Custom btcpayserver-docker branch")]
|
||||
public string BTCPayDockerBranch { get; set; }
|
||||
public List<string> AdditionalFragments { get; set; } = new List<string>();
|
||||
public List<string> ExcludedFragments { get; set; } = new List<string>();
|
||||
|
||||
public bool AnythingSet()
|
||||
{
|
||||
return !string.IsNullOrEmpty(CustomBTCPayImage) || !string.IsNullOrEmpty(BTCPayDockerRepository) ||
|
||||
!string.IsNullOrEmpty(BTCPayDockerBranch) || AdditionalFragments.Any() || ExcludedFragments.Any();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
namespace BTCPayServerDockerConfigurator.Models
|
||||
{
|
||||
public class AdvancedSettingsAdditionalData
|
||||
{
|
||||
public bool ShowSettings { get; set; } = false;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using BTCPayServerDockerConfigurator.Validation;
|
||||
|
||||
namespace BTCPayServerDockerConfigurator.Models
|
||||
{
|
||||
public class BTCTransmuterSettings
|
||||
{
|
||||
[RequiredIf(nameof(Enabled), "true", "A host must be set when enabled")]
|
||||
[Display(Name = "Hostname of your BTC Transmuter website")]
|
||||
public string Host { get; set; }
|
||||
public bool Enabled { get; set; }
|
||||
}
|
||||
}
|
||||
14
BTCPayServerDockerConfigurator/Models/ChainSettings.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace BTCPayServerDockerConfigurator.Models
|
||||
{
|
||||
public class ChainSettings
|
||||
{
|
||||
public bool Bitcoin { get; set; } = true;
|
||||
[Display(Name = "")] public List<string> AltChains { get; set; } = new List<string>();
|
||||
[Display(Name = "Pruning mode")] public PruneMode PruneMode { get; set; } = PruneMode.Small;
|
||||
|
||||
public NetworkType Network { get; set; } = NetworkType.Mainnet;
|
||||
}
|
||||
}
|
||||
130
BTCPayServerDockerConfigurator/Models/ConfiguratorSettings.cs
Normal file
@ -0,0 +1,130 @@
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace BTCPayServerDockerConfigurator.Models
|
||||
{
|
||||
public class ConfiguratorSettings
|
||||
{
|
||||
public DeploymentSettings DeploymentSettings { get; set; } = new DeploymentSettings();
|
||||
public DomainSettings DomainSettings { get; set; } = new DomainSettings();
|
||||
public LightningSettings LightningSettings { get; set; } = new LightningSettings();
|
||||
public AdvancedSettings AdvancedSettings { get; set; } = new AdvancedSettings();
|
||||
public ChainSettings ChainSettings { get; set; } = new ChainSettings();
|
||||
public AdditionalServices AdditionalServices { get; set; } = new AdditionalServices();
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return JsonSerializer.Serialize(this);
|
||||
}
|
||||
|
||||
public string ConstructBashFile(string downloadLink)
|
||||
{
|
||||
var result = new StringBuilder();
|
||||
|
||||
result.AppendLine(GetAbstractedPackageManager());
|
||||
result.AppendLine(InstallPackage("git wget"));
|
||||
DownloadFile(downloadLink);
|
||||
var gitRepo = string.IsNullOrEmpty(AdvancedSettings.BTCPayDockerRepository)
|
||||
? "https://github.com/btcpayserver/btcpayserver-docker" : AdvancedSettings.BTCPayDockerRepository;
|
||||
|
||||
var gitBranch = string.IsNullOrEmpty(AdvancedSettings.BTCPayDockerBranch)
|
||||
? "master" : AdvancedSettings.BTCPayDockerBranch;
|
||||
|
||||
result.AppendLine($"git clone -b {gitBranch} {gitRepo}");
|
||||
result.AppendLine($"cd btcpayserver-docker");
|
||||
if (!string.IsNullOrEmpty(AdvancedSettings.CustomBTCPayImage))
|
||||
{
|
||||
result.AppendLine($"export BTCPAY_IMAGE=\"{AdvancedSettings.CustomBTCPayImage}\"");
|
||||
}
|
||||
|
||||
if (gitBranch != "master" || gitRepo != "https://github.com/btcpayserver/btcpayserver-docker")
|
||||
{
|
||||
result.AppendLine($"export BTCPAYGEN_DOCKER_IMAGE=\"btcpayserver/docker-compose-generator:local\"");
|
||||
}
|
||||
|
||||
var additionalFragments = AdvancedSettings.AdditionalFragments;
|
||||
var excludedFragments = AdvancedSettings.ExcludedFragments;
|
||||
|
||||
result.AppendLine($"export BTCPAY_IMAGE=\"{AdvancedSettings.CustomBTCPayImage}\"");
|
||||
var domain = string.IsNullOrEmpty(DomainSettings.Domain) ? "btcpay.local" : DomainSettings.Domain;
|
||||
result.AppendLine($"export BTCPAY_HOST=\"{domain}\"");
|
||||
if (DomainSettings.AdditionalDomains.Any())
|
||||
{
|
||||
result.AppendLine($"export BTCPAY_ADDITIONAL_HOSTS=\"{string.Join(',', DomainSettings.AdditionalDomains)}\"");
|
||||
|
||||
}
|
||||
result.AppendLine($"export NBITCOIN_NETWORK=\"{ChainSettings.Network.ToString().ToLower()}\"");
|
||||
result.AppendLine($"export LIGHTNING_ALIAS=\"{LightningSettings.Alias}\"");
|
||||
result.AppendLine($"export BTCPAYGEN_LIGHTNING=\"{LightningSettings.Implementation}\"");
|
||||
var index = 1;
|
||||
if (ChainSettings.Bitcoin)
|
||||
{
|
||||
result.AppendLine($"export BTCPAYGEN_CRYPTO1=\"btc\"");
|
||||
index++;
|
||||
}
|
||||
|
||||
foreach (var chainSettingsAltChain in ChainSettings.AltChains)
|
||||
{
|
||||
result.AppendLine($"export BTCPAYGEN_CRYPTO{index}=\"{chainSettingsAltChain}\"");
|
||||
index++;
|
||||
}
|
||||
|
||||
result.AppendLine($"export BTCPAY_ENABLE_SSH=true");
|
||||
|
||||
if (AdditionalServices.LibrePatronSettings.Enabled)
|
||||
{
|
||||
additionalFragments.Add("opt-add-librepatron");
|
||||
result.AppendLine($"export LIBREPATRON_HOST=\"{AdditionalServices.LibrePatronSettings.Host}\"");
|
||||
}
|
||||
if (AdditionalServices.TorRelaySettings.Enabled)
|
||||
{
|
||||
additionalFragments.Add("opt-add-tor-relay");
|
||||
result.AppendLine($"export TOR_RELAY_NICKNAME=\"{AdditionalServices.TorRelaySettings.Nickname}\"");
|
||||
result.AppendLine($"export TOR_RELAY_EMAIL=\"{AdditionalServices.TorRelaySettings.Email}\"");
|
||||
}
|
||||
if (AdditionalServices.WooCommerceSettings.Enabled)
|
||||
{
|
||||
additionalFragments.Add("opt-add-woocommerce");
|
||||
result.AppendLine($"export WOOCOMMERCE_HOST=\"{AdditionalServices.WooCommerceSettings.Host}\"");
|
||||
}
|
||||
if (AdditionalServices.BTCTransmuterSettings.Enabled)
|
||||
{
|
||||
additionalFragments.Add("opt-add-btctransmuter");
|
||||
result.AppendLine($"export BTCTRANSMUTER_HOST=\"{AdditionalServices.BTCTransmuterSettings.Host}\"");
|
||||
}
|
||||
|
||||
if (additionalFragments.Any())
|
||||
{
|
||||
result.AppendLine($"export BTCPAYGEN_ADDITIONAL_FRAGMENTS=\"{string.Join(';', additionalFragments)}\"");
|
||||
}
|
||||
if (excludedFragments.Any())
|
||||
{
|
||||
result.AppendLine($"export BTCPAYGEN_EXCLUDE_FRAGMENTS=\"{string.Join(';', excludedFragments)}\"");
|
||||
}
|
||||
|
||||
result.AppendLine(". ./btcpay.setup -i");
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
private string GetAbstractedPackageManager()
|
||||
{
|
||||
return "#!/bin/bash\ndeclare -A osInfo;\nosInfo[/etc/debian_version]=\"apt-get install -y\"\nosInfo[/etc/alpine-release]=\"apk --update add\"\nosInfo[/etc/centos-release]=\"yum install -y\"\nosInfo[/etc/fedora-release]=\"dnf install -y\"\n\nfor f in ${!osInfo[@]}\ndo\n if [[ -f $f ]];then\n package_manager=${osInfo[$f]}\n fi\ndone";
|
||||
}
|
||||
|
||||
private string InstallPackage(string package)
|
||||
{
|
||||
return "${package_manager} "+ package;
|
||||
}
|
||||
|
||||
private string DownloadFile(string url)
|
||||
{
|
||||
if (string.IsNullOrEmpty(url))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
return $"wget {url} 2>/dev/null || curl -O {url}";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BTCPayServerDockerConfigurator.Models
|
||||
{
|
||||
public class DeploymentAdditionalData
|
||||
{
|
||||
public List<DeploymentType> AvailableDeploymentTypes { get; set; } = new List<DeploymentType>();
|
||||
}
|
||||
}
|
||||
22
BTCPayServerDockerConfigurator/Models/DeploymentSettings.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using BTCPayServerDockerConfigurator.Validation;
|
||||
|
||||
namespace BTCPayServerDockerConfigurator.Models
|
||||
{
|
||||
public class DeploymentSettings
|
||||
{
|
||||
[Required] public DeploymentType DeploymentType { get; set; }
|
||||
|
||||
[RequiredIf(nameof(DeploymentType), nameof(DeploymentType.RemoteMachine),
|
||||
"Please enter the host/ip of the remote server")]
|
||||
public string Host { get; set; }
|
||||
|
||||
[RequiredIf(nameof(DeploymentType), nameof(DeploymentType.RemoteMachine),
|
||||
"Please enter the username of the remote server")]
|
||||
public string Username { get; set; }
|
||||
|
||||
[RequiredIf(nameof(DeploymentType), nameof(DeploymentType.RemoteMachine),
|
||||
"Please enter the password of the remote server")]
|
||||
public string Password { get; set; }
|
||||
}
|
||||
}
|
||||
9
BTCPayServerDockerConfigurator/Models/DeploymentType.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace BTCPayServerDockerConfigurator.Models
|
||||
{
|
||||
public enum DeploymentType
|
||||
{
|
||||
ThisMachine,
|
||||
RemoteMachine,
|
||||
Manual
|
||||
}
|
||||
}
|
||||
10
BTCPayServerDockerConfigurator/Models/DomainSettings.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BTCPayServerDockerConfigurator.Models
|
||||
{
|
||||
public class DomainSettings
|
||||
{
|
||||
public string Domain { get; set; }
|
||||
public List<string> AdditionalDomains { get; set; } = new List<string>();
|
||||
}
|
||||
}
|
||||
16
BTCPayServerDockerConfigurator/Models/EnumTextHelper.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BTCPayServerDockerConfigurator.Models
|
||||
{
|
||||
public static class EnumTextHelper
|
||||
{
|
||||
public static Dictionary<PruneMode, string> PruneMode = new Dictionary<PruneMode, string>()
|
||||
{
|
||||
{Models.PruneMode.NoPruning, "No pruning"},
|
||||
{Models.PruneMode.Minimal, "100GB ~1 year worth of blocks"},
|
||||
{Models.PruneMode.Small, "50GB ~6 months worth of blocks"},
|
||||
{Models.PruneMode.ExtraSmall, "25GB ~3 months worth of blocks"},
|
||||
{Models.PruneMode.ExtraExtraSmall, "5GB ~2 weeks worth of blocks"},
|
||||
};
|
||||
}
|
||||
}
|
||||
12
BTCPayServerDockerConfigurator/Models/ErrorViewModel.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using BTCPayServerDockerConfigurator.Controllers;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace BTCPayServerDockerConfigurator.Models
|
||||
{
|
||||
public class ErrorViewModel
|
||||
{
|
||||
public string RequestId { get; set; }
|
||||
|
||||
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||
}
|
||||
}
|
||||
13
BTCPayServerDockerConfigurator/Models/LibrePatronSettings.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using BTCPayServerDockerConfigurator.Validation;
|
||||
|
||||
namespace BTCPayServerDockerConfigurator.Models
|
||||
{
|
||||
public class LibrePatronSettings
|
||||
{
|
||||
[RequiredIf(nameof(Enabled), "true", "A host must be set when enabled")]
|
||||
[Display(Name = "Hostname of your Libre Patron website")]
|
||||
public string Host { get; set; }
|
||||
public bool Enabled { get; set; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
namespace BTCPayServerDockerConfigurator.Models
|
||||
{
|
||||
public class LightningSettings
|
||||
{
|
||||
public string Implementation { get; set; } = "";
|
||||
public string Alias { get; set; }
|
||||
}
|
||||
}
|
||||
9
BTCPayServerDockerConfigurator/Models/NetworkType.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace BTCPayServerDockerConfigurator.Models
|
||||
{
|
||||
public enum NetworkType
|
||||
{
|
||||
Mainnet,
|
||||
Testnet,
|
||||
Regtest
|
||||
}
|
||||
}
|
||||
48
BTCPayServerDockerConfigurator/Models/Options.cs
Normal file
@ -0,0 +1,48 @@
|
||||
namespace BTCPayServerDockerConfigurator.Models
|
||||
{
|
||||
public class Options
|
||||
{
|
||||
public string SSHConnection { get; set; } = null;
|
||||
public string SSHPassword { get; set; } = "";
|
||||
public string SSHKeyFile { get; set; }= "";
|
||||
public string SSHAuthorizedKeys { get; set; }= "";
|
||||
public string SSHKeyFilePassword { get; set; }= "";
|
||||
|
||||
public SSHSettings ParseSSHConfiguration()
|
||||
{
|
||||
var settings = new SSHSettings()
|
||||
{
|
||||
Password = SSHPassword,
|
||||
KeyFile = SSHKeyFile,
|
||||
AuthorizedKeysFile = SSHAuthorizedKeys,
|
||||
KeyFilePassword = SSHKeyFilePassword,
|
||||
Server = SSHConnection
|
||||
};
|
||||
if (settings.Server != null)
|
||||
{
|
||||
var parts = settings.Server.Split(':');
|
||||
if (parts.Length == 2 && int.TryParse(parts[1], out int port))
|
||||
{
|
||||
settings.Port = port;
|
||||
settings.Server = parts[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
settings.Port = 22;
|
||||
}
|
||||
|
||||
parts = settings.Server.Split('@');
|
||||
if (parts.Length == 2)
|
||||
{
|
||||
settings.Username = parts[0];
|
||||
settings.Server = parts[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
settings.Username = "root";
|
||||
}
|
||||
}
|
||||
return settings;
|
||||
}
|
||||
}
|
||||
}
|
||||
12
BTCPayServerDockerConfigurator/Models/PruneMode.cs
Normal file
@ -0,0 +1,12 @@
|
||||
namespace BTCPayServerDockerConfigurator.Models
|
||||
{
|
||||
public enum PruneMode
|
||||
{
|
||||
|
||||
NoPruning = 0,
|
||||
Minimal = 100,
|
||||
Small = 50,
|
||||
ExtraSmall = 25,
|
||||
ExtraExtraSmall = 5,
|
||||
}
|
||||
}
|
||||
127
BTCPayServerDockerConfigurator/Models/SSHClientExtensions.cs
Normal file
@ -0,0 +1,127 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Renci.SshNet;
|
||||
|
||||
namespace BTCPayServerDockerConfigurator.Models
|
||||
{
|
||||
public static class SSHClientExtensions
|
||||
{
|
||||
public static async Task<SshClient> ConnectAsync(this SSHSettings sshSettings, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (sshSettings == null)
|
||||
throw new ArgumentNullException(nameof(sshSettings));
|
||||
TaskCompletionSource<SshClient> tcs = new TaskCompletionSource<SshClient>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
new Thread(() =>
|
||||
{
|
||||
SshClient sshClient = null;
|
||||
try
|
||||
{
|
||||
sshClient = new SshClient(sshSettings.CreateConnectionInfo());
|
||||
sshClient.HostKeyReceived += (object sender, Renci.SshNet.Common.HostKeyEventArgs e) =>
|
||||
{
|
||||
e.CanTrust = true;
|
||||
};
|
||||
sshClient.Connect();
|
||||
tcs.TrySetResult(sshClient);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
tcs.TrySetException(ex);
|
||||
try
|
||||
{
|
||||
sshClient?.Dispose();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
})
|
||||
{ IsBackground = true }.Start();
|
||||
|
||||
using (cancellationToken.Register(() => { tcs.TrySetCanceled(); }))
|
||||
{
|
||||
return await tcs.Task;
|
||||
}
|
||||
}
|
||||
|
||||
public static string EscapeSingleQuotes(this string command)
|
||||
{
|
||||
return command.Replace("'", "'\"'\"'", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public static Task<SSHCommandResult> RunBash(this SshClient sshClient, string command, TimeSpan? timeout = null)
|
||||
{
|
||||
if (sshClient == null)
|
||||
throw new ArgumentNullException(nameof(sshClient));
|
||||
if (command == null)
|
||||
throw new ArgumentNullException(nameof(command));
|
||||
command = $"bash -c '{command.EscapeSingleQuotes()}'";
|
||||
var sshCommand = sshClient.CreateCommand(command);
|
||||
if (timeout is TimeSpan v)
|
||||
sshCommand.CommandTimeout = v;
|
||||
var tcs = new TaskCompletionSource<SSHCommandResult>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
new Thread(() =>
|
||||
{
|
||||
sshCommand.BeginExecute(ar =>
|
||||
{
|
||||
try
|
||||
{
|
||||
sshCommand.EndExecute(ar);
|
||||
tcs.TrySetResult(CreateSSHCommandResult(sshCommand));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
tcs.TrySetException(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
sshCommand.Dispose();
|
||||
}
|
||||
});
|
||||
})
|
||||
{ IsBackground = true }.Start();
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
private static SSHCommandResult CreateSSHCommandResult(SshCommand sshCommand)
|
||||
{
|
||||
return new SSHCommandResult()
|
||||
{
|
||||
Output = sshCommand.Result,
|
||||
Error = sshCommand.Error,
|
||||
ExitStatus = sshCommand.ExitStatus
|
||||
};
|
||||
}
|
||||
|
||||
public static async Task DisconnectAsync(this SshClient sshClient, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (sshClient == null)
|
||||
throw new ArgumentNullException(nameof(sshClient));
|
||||
|
||||
TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
new Thread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
sshClient.Disconnect();
|
||||
tcs.TrySetResult(true);
|
||||
}
|
||||
catch
|
||||
{
|
||||
tcs.TrySetResult(true); // We don't care about exception
|
||||
}
|
||||
})
|
||||
{ IsBackground = true }.Start();
|
||||
using (cancellationToken.Register(() => tcs.TrySetCanceled()))
|
||||
{
|
||||
await tcs.Task;
|
||||
}
|
||||
}
|
||||
|
||||
public class SSHCommandResult
|
||||
{
|
||||
public int ExitStatus { get; internal set; }
|
||||
public string Output { get; internal set; }
|
||||
public string Error { get; internal set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
27
BTCPayServerDockerConfigurator/Models/SSHSettings.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using Renci.SshNet;
|
||||
|
||||
namespace BTCPayServerDockerConfigurator.Models
|
||||
{
|
||||
public class SSHSettings
|
||||
{
|
||||
public string Server { get; set; }
|
||||
public int Port { get; set; } = 22;
|
||||
public string KeyFile { get; set; }
|
||||
public string KeyFilePassword { get; set; }
|
||||
public string AuthorizedKeysFile { get; set; }
|
||||
public string Username { get; set; }
|
||||
public string Password { get; set; }
|
||||
|
||||
public ConnectionInfo CreateConnectionInfo()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(KeyFile))
|
||||
{
|
||||
return new ConnectionInfo(Server, Port, Username, new[] { new PrivateKeyAuthenticationMethod(Username, new PrivateKeyFile(KeyFile, KeyFilePassword)) });
|
||||
}
|
||||
else
|
||||
{
|
||||
return new ConnectionInfo(Server, Port, Username, new[] { new PasswordAuthenticationMethod(Username, Password) });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
17
BTCPayServerDockerConfigurator/Models/TorRelaySettings.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using BTCPayServerDockerConfigurator.Validation;
|
||||
|
||||
namespace BTCPayServerDockerConfigurator.Models
|
||||
{
|
||||
public class TorRelaySettings
|
||||
{
|
||||
[RequiredIf(nameof(Enabled), "true", "Required")]
|
||||
[Display(Name = "Relay nickname")]
|
||||
public string Nickname { get; set; }
|
||||
[RequiredIf(nameof(Enabled), "true", "Required")]
|
||||
[EmailAddress]
|
||||
[Display(Name = "Tor contact email")]
|
||||
public string Email { get; set; }
|
||||
public bool Enabled { get; set; }
|
||||
}
|
||||
}
|
||||
9
BTCPayServerDockerConfigurator/Models/UpdateSettings.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace BTCPayServerDockerConfigurator.Models
|
||||
{
|
||||
public class UpdateSettings<T,TAdditionalData>
|
||||
{
|
||||
public T Settings { get; set; }
|
||||
public TAdditionalData Additional { get; set; }
|
||||
public string Json { get; set; }
|
||||
}
|
||||
}
|
||||
13
BTCPayServerDockerConfigurator/Models/WooCommerceSettings.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using BTCPayServerDockerConfigurator.Validation;
|
||||
|
||||
namespace BTCPayServerDockerConfigurator.Models
|
||||
{
|
||||
public class WooCommerceSettings
|
||||
{
|
||||
[RequiredIf(nameof(Enabled), "true", "A host must be set when enabled")]
|
||||
[Display(Name = "Hostname of your WooCommerce website")]
|
||||
public string Host { get; set; }
|
||||
public bool Enabled { get; set; }
|
||||
}
|
||||
}
|
||||
23
BTCPayServerDockerConfigurator/Program.cs
Normal file
@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace BTCPayServerDockerConfigurator
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
CreateHostBuilder(args).Build().Run();
|
||||
}
|
||||
|
||||
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||
Host.CreateDefaultBuilder(args)
|
||||
.ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
{
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:30600",
|
||||
"sslPort": 44345
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"BTCPayServerDockerConfigurator": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "https://localhost:5001;http://localhost:5000",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
61
BTCPayServerDockerConfigurator/Startup.cs
Normal file
@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServerDockerConfigurator.Models;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.HttpsPolicy;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace BTCPayServerDockerConfigurator
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
public Startup(IConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; }
|
||||
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddOptions();
|
||||
services.Configure<Options>(Configuration);
|
||||
services.AddControllersWithViews().AddRazorRuntimeCompilation();
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
{
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
else
|
||||
{
|
||||
app.UseExceptionHandler("/Home/Error");
|
||||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
||||
app.UseHsts();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
app.UseStaticFiles();
|
||||
|
||||
app.UseRouting();
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapControllerRoute(
|
||||
name: "default",
|
||||
pattern: "{controller=Home}/{action=Index}/{id?}");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
32
BTCPayServerDockerConfigurator/Validation/aa.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace BTCPayServerDockerConfigurator.Validation
|
||||
{
|
||||
//from https://stackoverflow.com/questions/52321148/conditional-validation-in-mvc-net-core-requiredif
|
||||
public class RequiredIfAttribute : ValidationAttribute
|
||||
{
|
||||
private String PropertyName { get; set; }
|
||||
private String ErrorMessage { get; set; }
|
||||
private Object DesiredValue { get; set; }
|
||||
|
||||
public RequiredIfAttribute(String propertyName, Object desiredvalue, String errormessage)
|
||||
{
|
||||
this.PropertyName = propertyName;
|
||||
this.DesiredValue = desiredvalue;
|
||||
this.ErrorMessage = errormessage;
|
||||
}
|
||||
|
||||
protected override ValidationResult IsValid(object value, ValidationContext context)
|
||||
{
|
||||
Object instance = context.ObjectInstance;
|
||||
Type type = instance.GetType();
|
||||
Object proprtyvalue = type.GetProperty(PropertyName).GetValue(instance, null);
|
||||
if (proprtyvalue.ToString() == DesiredValue.ToString() && value == null)
|
||||
{
|
||||
return new ValidationResult(ErrorMessage);
|
||||
}
|
||||
return ValidationResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
@model BTCTransmuterSettings
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6 col-sm-12 offset-md-3 ">
|
||||
<div class="card shadow-sm ">
|
||||
<label class="d-flex justify-content-between px-2 pb-0 mb-0 styled-checkbox-label">
|
||||
<h3 class="card-title text-center pt-2 pb-0 mb-0">BTC Transmuter</h3>
|
||||
<input class="styled-checkbox" type="checkbox" asp-for="Enabled"/>
|
||||
</label>
|
||||
|
||||
<div class="card-body">
|
||||
<p class="card-text">
|
||||
BTC Transmuter is an opt-in plugin for BTCPay that allows you to automate workflows around your bitcoin. You'll be able to automate trades on exchanges, send customized emails, set up hot wallets for transaction automization and even handle lighrning channels.
|
||||
</p>
|
||||
<div id="transmuter-body" style="display: none">
|
||||
<hr>
|
||||
<div class="form-group">
|
||||
<label asp-for="Host"></label>
|
||||
<input class="form-control" asp-for="Host">
|
||||
<span asp-validation-for="Host" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,28 @@
|
||||
@model LibrePatronSettings
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6 col-sm-12 offset-md-3 ">
|
||||
<div class="card shadow-sm ">
|
||||
<label class="d-flex justify-content-between px-2 pb-0 mb-0 styled-checkbox-label">
|
||||
|
||||
<h3 class="card-title text-center pt-2">LibrePatron</h3>
|
||||
|
||||
<input class="styled-checkbox" type="checkbox" asp-for="Enabled"/>
|
||||
</label>
|
||||
|
||||
<div class="card-body">
|
||||
<p class="card-text">
|
||||
LibrePatron is A self-hosted Patreon alternative backed by BTCPay created by Jeff Vandrew Jr.
|
||||
</p>
|
||||
<div id="librepatron-body" style="display: none">
|
||||
<hr>
|
||||
<div class="form-group">
|
||||
<label asp-for="Host"></label>
|
||||
<input class="form-control" asp-for="Host">
|
||||
<span asp-validation-for="Host" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,32 @@
|
||||
@model TorRelaySettings
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6 col-sm-12 offset-md-3 ">
|
||||
<div class="card shadow-sm ">
|
||||
<label class="d-flex justify-content-between px-2 pb-0 mb-0 styled-checkbox-label">
|
||||
|
||||
<h3 class="card-title text-center pt-2">Tor Relay </h3>
|
||||
|
||||
<input class="styled-checkbox" type="checkbox" asp-for="Enabled"/>
|
||||
</label>
|
||||
<div class="card-body">
|
||||
<p class="card-text">
|
||||
Tor relays are also referred to as "routers" or "nodes." They receive traffic on the Tor network and pass it along. This sets up a non-exit tor relay alongside your BTCPay. <br/><a href="https://www.eff.org/torchallenge/faq.html" target="_parent">Please read the legal implications of running a tor relay</a> and <a href="https://trac.torproject.org/projects/tor/wiki/TorRelayGuide#RelayRequirements" target="_blank">what resources are used to operate the relay.</a>
|
||||
</p>
|
||||
<div id="tor-relay-body" style="display: none">
|
||||
<hr>
|
||||
<div class="form-group">
|
||||
<label asp-for="Email"></label>
|
||||
<input class="form-control" asp-for="Email">
|
||||
<span asp-validation-for="Email" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Nickname"></label>
|
||||
<input class="form-control" asp-for="Nickname">
|
||||
<span asp-validation-for="Nickname" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,27 @@
|
||||
@model WooCommerceSettings
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6 col-sm-12 offset-md-3 ">
|
||||
<div class="card shadow-sm ">
|
||||
<label class="d-flex justify-content-between px-2 pb-0 mb-0 styled-checkbox-label">
|
||||
|
||||
<h3 class="card-title text-center pt-2">WooCommerce</h3>
|
||||
|
||||
<input class="styled-checkbox" type="checkbox" asp-for="Enabled"/>
|
||||
</label>
|
||||
<div class="card-body">
|
||||
<p class="card-text">
|
||||
WooCommerce is an open-source e-commerce plugin for WordPress. This will install Wordpress + Woocommerce plugin + BTCPay plugin.
|
||||
</p>
|
||||
<div id="woocommerce-body" style="display: none">
|
||||
<hr>
|
||||
<div class="form-group">
|
||||
<label asp-for="Host"></label>
|
||||
<input class="form-control" asp-for="Host">
|
||||
<span asp-validation-for="Host" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,57 @@
|
||||
@{
|
||||
ViewData["Title"] = "How about some extra icing on top?";
|
||||
}
|
||||
@model UpdateSettings<AdditionalServices, AdditionalDataStub>;
|
||||
<form class="container" method="post" asp-action="AdditionalServices">
|
||||
@* <div asp-validation-summary="All" class="text-danger"></div> *@
|
||||
<input type="hidden" asp-for="Json"/>
|
||||
<div class="row mb-md-4">
|
||||
<h2 class=" text-center w-100">@ViewData["Title"]</h2>
|
||||
</div>
|
||||
|
||||
<partial for="Settings.TorRelaySettings" name="Additional/TorRelaySettings"/>
|
||||
<partial for="Settings.BTCTransmuterSettings" name="Additional/BTCTransmuterSettings"/>
|
||||
<partial for="Settings.LibrePatronSettings" name="Additional/LibrePatronSettings"/>
|
||||
<partial for="Settings.WooCommerceSettings" name="Additional/WooCommerceSettings"/>
|
||||
<div class="row w-100">
|
||||
<button type="submit" class="m-auto btn btn-primary btn-lg">Continue</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
@section Scripts
|
||||
{
|
||||
<script>
|
||||
|
||||
$(document).ready(function(){
|
||||
var torBody = $("#tor-relay-body");
|
||||
var torInput = $("#Settings_TorRelaySettings_Enabled");
|
||||
toggle(torInput, torBody);
|
||||
torInput.on("change input", function(){ toggle(torInput, torBody)});
|
||||
|
||||
var transmuterBody = $("#transmuter-body");
|
||||
var transmuterInput = $("#Settings_BTCTransmuterSettings_Enabled");
|
||||
toggle(transmuterInput, transmuterBody);
|
||||
transmuterInput.on("change input", function(){ toggle(transmuterInput, transmuterBody)});
|
||||
|
||||
var librepatronBody = $("#librepatron-body");
|
||||
var librepatronInput = $("#Settings_LibrePatronSettings_Enabled");
|
||||
toggle(librepatronInput, librepatronBody);
|
||||
librepatronInput.on("change input", function(){ toggle(librepatronInput, librepatronBody)});
|
||||
|
||||
var woocommerceBody = $("#woocommerce-body");
|
||||
var woocommerceInput = $("#Settings_WooCommerceSettings_Enabled");
|
||||
toggle(woocommerceInput, woocommerceBody);
|
||||
woocommerceInput.on("change input", function(){ toggle(woocommerceInput, woocommerceBody)});
|
||||
});
|
||||
|
||||
function toggle(input, el){
|
||||
if(input.prop('checked')){
|
||||
el.show();
|
||||
}else{
|
||||
el.hide();
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
}
|
||||
@ -0,0 +1,109 @@
|
||||
@{
|
||||
ViewData["Title"] = "Geeky settings?";
|
||||
}
|
||||
@model UpdateSettings<AdvancedSettings, AdvancedSettingsAdditionalData>;
|
||||
<form class="container" method="post" asp-action="AdvancedSettings">
|
||||
@* <div asp-validation-summary="All" class="text-danger"></div> *@
|
||||
<input type="hidden" asp-for="Json"/>
|
||||
<div class="row mb-md-4">
|
||||
<h2 class=" text-center w-100">@ViewData["Title"]</h2>
|
||||
</div>
|
||||
|
||||
@if (!Model.Additional.ShowSettings)
|
||||
{
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6 col-sm-12 offset-md-3 card shadow-sm">
|
||||
<div class="card-body w-100 text-center">
|
||||
<button type="submit" class="btn btn-primary ">
|
||||
Continue
|
||||
</button>
|
||||
<br/>
|
||||
<br/>
|
||||
<button type="submit" name="command" value="show" class="btn btn-link h2 w-100 text-center">Show me the advanced settings.</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="row mb-4 @(Model.Additional.ShowSettings ? string.Empty : "invisible")">
|
||||
<div class="col-md-6 col-sm-12 offset-md-3 card shadow-sm pt-4">
|
||||
<h3 class="card-title">Additional fragments</h3>
|
||||
<ul class="list-group list-group-flush" style="list-style-type: none">
|
||||
@for (var index = 0; index < Model.Settings.AdditionalFragments.Count; index++)
|
||||
{
|
||||
<li class="list-group-item justify-content-between align-items-center">
|
||||
<div class="form-group">
|
||||
<div class="input-group mb-3">
|
||||
<input class="form-control" asp-for="Settings.AdditionalFragments[index]" placeholder="Fragment">
|
||||
<div class="input-group-append">
|
||||
<button type="submit" name="command" value="@($"remove-additional:{index}")" class="btn btn-outline-secondary">Remove</button>
|
||||
</div>
|
||||
</div>
|
||||
<span asp-validation-for="Settings.AdditionalFragments[index]" class="text-danger"></span>
|
||||
</div>
|
||||
</li>
|
||||
}
|
||||
<li class="list-group-item-action text-center">
|
||||
<button name="command" value="add-additional" class="btn btn-link text-black-50">Add additional fragment</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-4 @(Model.Additional.ShowSettings ? string.Empty : "invisible")">
|
||||
<div class="col-md-6 col-sm-12 offset-md-3 card shadow-sm pt-4">
|
||||
<h3 class="card-title">Excluded fragments</h3>
|
||||
<ul class="list-group list-group-flush" style="list-style-type: none">
|
||||
@for (var index = 0; index < Model.Settings.ExcludedFragments.Count; index++)
|
||||
{
|
||||
<li class="list-group-item justify-content-between align-items-center">
|
||||
<div class="form-group">
|
||||
<div class="input-group mb-3">
|
||||
<input class="form-control" asp-for="Settings.ExcludedFragments[index]" placeholder="Fragment">
|
||||
<div class="input-group-append">
|
||||
<button type="submit" name="command" value="@($"remove-excluded:{index}")" class="btn btn-outline-secondary">Remove</button>
|
||||
</div>
|
||||
</div>
|
||||
<span asp-validation-for="Settings.ExcludedFragments[index]" class="text-danger"></span>
|
||||
</div>
|
||||
</li>
|
||||
}
|
||||
<li class="list-group-item-action text-center">
|
||||
<button name="command" value="add-excluded" class="btn btn-link text-black-50">Add excluded fragment</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-4 @(Model.Additional.ShowSettings ? string.Empty : "invisible")">
|
||||
<div class="col-md-6 col-sm-12 offset-md-3 ">
|
||||
<div class="card shadow-sm ">
|
||||
<h3 class="card-title text-center pt-2">Fork settings</h3>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label asp-for="Settings.BTCPayDockerRepository"></label>
|
||||
<input class="form-control" asp-for="Settings.BTCPayDockerRepository">
|
||||
<span asp-validation-for="Settings.BTCPayDockerRepository" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Settings.BTCPayDockerBranch"></label>
|
||||
<input class="form-control" asp-for="Settings.BTCPayDockerBranch">
|
||||
<span asp-validation-for="Settings.BTCPayDockerBranch" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Settings.CustomBTCPayImage"></label>
|
||||
<input class="form-control" asp-for="Settings.CustomBTCPayImage">
|
||||
<span asp-validation-for="Settings.CustomBTCPayImage" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row @(Model.Additional.ShowSettings ? string.Empty : "invisible")">
|
||||
<div class="col-md-6 col-sm-12 offset-md-3 text-center">
|
||||
<button type="submit" class="m-auto btn btn-primary btn-lg">Continue</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@ -0,0 +1,117 @@
|
||||
@{
|
||||
ViewData["Title"] = "How do your want your nodes?";
|
||||
}
|
||||
@model UpdateSettings<ChainSettings, AdditionalDataStub>;
|
||||
<form class="container" method="post" asp-action="ChainSettings">
|
||||
@* <div asp-validation-summary="All" class="text-danger"></div> *@
|
||||
<input type="hidden" asp-for="Json"/>
|
||||
<div class="row mb-md-4">
|
||||
<h2 class=" text-center w-100">@ViewData["Title"]</h2>
|
||||
</div>
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6 col-sm-12 offset-md-3">
|
||||
<label class="card-input-element-label">
|
||||
<input type="checkbox" asp-for="Settings.Bitcoin" class="card-input-element d-none"/>
|
||||
<div class="card shadow-sm w-100">
|
||||
<div class="card-img-top bg-light" style="position:relative; background-image: url(assets/Bitcoin.svg); background-repeat: no-repeat ; background-size: contain ;background-position: center; width: 100%; height: 225px"></div>
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-center">Bitcoin</h3>
|
||||
<p class="card-text">A full Bitcoin node will de deployed alongside BTCPay in order to validate your incoming transactions without third parties.</p>
|
||||
<span asp-validation-for="Settings.Bitcoin" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6 col-sm-12 offset-md-3 card shadow-sm ">
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label asp-for="Settings.Network"></label>
|
||||
<select class="form-control" asp-for="Settings.Network">
|
||||
<option value="@NetworkType.Mainnet">Mainnet</option>
|
||||
<option value="@NetworkType.Testnet">Testnet</option>
|
||||
<option value="@NetworkType.Regtest">Regtest</option>
|
||||
</select>
|
||||
<span asp-validation-for="Settings.Network" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Settings.PruneMode"></label>
|
||||
<select class="form-control" asp-for="Settings.PruneMode">
|
||||
<option value="@PruneMode.NoPruning">@EnumTextHelper.PruneMode[PruneMode.NoPruning]</option>
|
||||
<option value="@PruneMode.Minimal">@EnumTextHelper.PruneMode[PruneMode.Minimal]</option>
|
||||
<option value="@PruneMode.Small">@EnumTextHelper.PruneMode[PruneMode.Small]</option>
|
||||
<option value="@PruneMode.ExtraSmall">@EnumTextHelper.PruneMode[PruneMode.ExtraSmall]</option>
|
||||
<option value="@PruneMode.ExtraExtraSmall">@EnumTextHelper.PruneMode[PruneMode.ExtraExtraSmall]</option>
|
||||
</select>
|
||||
<small class="text-muted">Bitcoin uses up quite a bit of data. Fortunately, there is pruning mode which lets you operate uyour full node while only keeping a certain amount of the latest blocks.</small>
|
||||
<span asp-validation-for="Settings.PruneMode" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6 col-sm-12 offset-md-3 @(Model.Settings.AltChains.Any() ? "card shadow-sm pt-4" : string.Empty)">
|
||||
<ul class="list-group list-group-flush" style="list-style-type: none">
|
||||
@for (var index = 0; index < Model.Settings.AltChains.Count; index++)
|
||||
{
|
||||
<li class="list-group-item justify-content-between align-items-center">
|
||||
<div class="form-group">
|
||||
<div class="input-group mb-3">
|
||||
<input class="form-control" asp-for="Settings.AltChains[index]" placeholder="Crypto Code">
|
||||
<div class="input-group-append">
|
||||
<button type="submit" name="command" value="@($"remove-chain:{index}")" class="btn btn-outline-secondary">Remove</button>
|
||||
</div>
|
||||
</div>
|
||||
<span asp-validation-for="Settings.AltChains[index]" class="text-danger"></span>
|
||||
</div>
|
||||
</li>
|
||||
}
|
||||
<li class="list-group-item-action text-center">
|
||||
<button name="command" value="add-chain" class="btn btn-link text-black-50">Add an altcoin</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-sm-12 offset-md-3 text-center">
|
||||
<button type="submit" class="m-auto btn btn-primary btn-lg">Continue</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="modal fade" id="warningModal" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">⚠ Lightning is not supported with this level of pruning.</h5>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts
|
||||
{
|
||||
<script>
|
||||
var warningToggled = false;
|
||||
$(document).ready(function() {
|
||||
var select = $("#Settings_PruneMode");
|
||||
select.on("change input", function(){ toggleWarning(select)});
|
||||
});
|
||||
|
||||
function toggleWarning(select){
|
||||
var pruning = select.val();
|
||||
if(pruning === "@PruneMode.ExtraExtraSmall"){
|
||||
$("#warningModal").modal("show");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
@model UpdateSettings<ConfiguratorSettings, BTCPayServerDockerConfigurator.Controllers.DeployAdditionalData>
|
||||
|
||||
|
||||
|
||||
<code>
|
||||
@Json.Serialize(Model.Additional)
|
||||
</code>
|
||||
@ -0,0 +1,102 @@
|
||||
@{
|
||||
ViewData["Title"] = "What BTCPay Server are you configuring?";
|
||||
}
|
||||
@model UpdateSettings<DeploymentSettings, DeploymentAdditionalData>;
|
||||
<form class="container" method="post" asp-action="DeploymentDestination">
|
||||
@* <div asp-validation-summary="All" class="text-danger"></div> *@
|
||||
<input type="hidden" asp-for="Json"/>
|
||||
<div class="row mb-md-4">
|
||||
<h2 class=" text-center w-100">@ViewData["Title"]</h2>
|
||||
</div>
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-4">
|
||||
<label class="card-input-element-label">
|
||||
<input type="radio" asp-for="Settings.DeploymentType" value="@DeploymentType.ThisMachine" class="card-input-element d-none"/>
|
||||
<div class="card shadow-sm w-100">
|
||||
<div class="card-img-top bg-light" style="background-image: url(assets/machine.svg); background-repeat: no-repeat ; background-size: contain ;background-position: center; width: 100%; height: 225px"></div>
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-center">This machine</h3>
|
||||
<p class="card-text text-center">
|
||||
We'll configure BTCPay on the current machine
|
||||
<br/><br/><br/>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="card-input-element-label">
|
||||
<input type="radio" asp-for="Settings.DeploymentType" value="@DeploymentType.RemoteMachine" class="card-input-element d-none"/>
|
||||
<div class="card shadow-sm w-100">
|
||||
<div class="card-img-top bg-light" style="position:relative; background-image: url(assets/remote.svg); background-repeat: no-repeat ; background-size: contain ;background-position: center; width: 100%; height: 225px"></div>
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-center">A remote machine</h3>
|
||||
<p class="card-text">A remote machine that you can provide the SSH connectivity details to so that the configurator automagically sets it up for you</p>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="card-input-element-label">
|
||||
<input type="radio" asp-for="Settings.DeploymentType" value="@DeploymentType.Manual" class="card-input-element d-none"/>
|
||||
<div class="card shadow-sm w-100 ">
|
||||
<div class="card-img-top bg-light" style="position:relative; background-image: url(assets/shell.png); background-repeat: no-repeat ; background-size: contain ;background-position: center; width: 100%; height: 225px"></div>
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-center">Manual</h3>
|
||||
<p class="card-text">We'll generate a bash script that you run on the server without ever providing us access.<br/><br/></p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<span asp-validation-for="Settings.DeploymentType" class="text-danger w-100 text-center"></span>
|
||||
</div>
|
||||
<div class="row mb-4" style="display: none" id="remote-machine-form">
|
||||
<div class="col-md-6 col-sm-12 offset-md-3 ">
|
||||
<div class="card shadow-sm ">
|
||||
<h3 class="card-title text-center pt-2">Remote Machine SSH Details</h3>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label asp-for="Settings.Host"></label>
|
||||
<input class="form-control" asp-for="Settings.Host">
|
||||
<span asp-validation-for="Settings.Host" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Settings.Username"></label>
|
||||
<input class="form-control" asp-for="Settings.Username">
|
||||
<span asp-validation-for="Settings.Username" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Settings.Password">Password</label>
|
||||
<input type="password" class="form-control" asp-for="Settings.Password" placeholder="Password">
|
||||
<span asp-validation-for="Settings.Password" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row w-100">
|
||||
<button type="submit" class="m-auto btn btn-primary btn-lg">Continue</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@section Scripts
|
||||
{
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
toggleForm();
|
||||
$("label").on("click", toggleForm);
|
||||
});
|
||||
|
||||
function toggleForm(){
|
||||
var selectedDeploymentType = $('input[name=Settings\\.DeploymentType]:checked').val();
|
||||
if(selectedDeploymentType === "@nameof(DeploymentType.RemoteMachine)"){
|
||||
$("#remote-machine-form").show();
|
||||
}else{
|
||||
$("#remote-machine-form").hide();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
@{
|
||||
ViewData["Title"] = "How will you be accessing your BTCPay Server?";
|
||||
}
|
||||
@model UpdateSettings<DomainSettings, AdditionalDataStub>;
|
||||
<form class="container" method="post" asp-action="DomainSettings">
|
||||
@* <div asp-validation-summary="All" class="text-danger"></div> *@
|
||||
<input type="hidden" asp-for="Json"/>
|
||||
<div class="row mb-md-4">
|
||||
<h2 class=" text-center w-100">@ViewData["Title"]</h2>
|
||||
</div>
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6 col-sm-12 offset-md-3 ">
|
||||
<div class="card shadow-sm ">
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label asp-for="Settings.Domain"></label>
|
||||
<input class="form-control" asp-for="Settings.Domain">
|
||||
<small class="text-muted">If you don't have a domain to use, you'll still be able to access BTCPay and other related services via TOR. <a href="https://docs.btcpayserver.org/features/dynamicdns" target="_blank">There's also built-in support for dynamic DNS providers.</a></small>
|
||||
<span asp-validation-for="Settings.Domain" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<ul class="list-group list-group-flush" style="list-style-type: none">
|
||||
@for (var index = 0; index < Model.Settings.AdditionalDomains.Count; index++)
|
||||
{
|
||||
<li class="list-group-item justify-content-between align-items-center">
|
||||
<div class="form-group">
|
||||
<label asp-for="Settings.AdditionalDomains[index]"></label>
|
||||
<div class="input-group mb-3">
|
||||
|
||||
<input class="form-control" asp-for="Settings.AdditionalDomains[index]">
|
||||
<div class="input-group-append">
|
||||
<button type="submit" name="command" value="@($"remove-domain:{index}")" class="btn btn-outline-secondary">Remove</button>
|
||||
</div>
|
||||
</div>
|
||||
<span asp-validation-for="Settings.AdditionalDomains[index]" class="text-danger"></span>
|
||||
</div>
|
||||
</li>
|
||||
}
|
||||
<li class="list-group-item-action text-center" style="visibility: hidden" id="btn-additional-domain">
|
||||
<button name="command" value="add-domain" class="btn btn-sm">Add an additional domain</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row w-100">
|
||||
<button type="submit" class="m-auto btn btn-primary btn-lg">Continue</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@section Scripts
|
||||
{
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
var btn = $("#btn-additional-domain");
|
||||
var input = $("#Settings_Domain");
|
||||
toggleBtn(btn, input);
|
||||
input.on("change input", function(){ toggleBtn(btn, input)});
|
||||
});
|
||||
|
||||
function toggleBtn(btn,input){
|
||||
if(input.val()){
|
||||
btn.css("visibility", "visible");
|
||||
}else{
|
||||
btn.css("visibility", "hidden");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
}
|
||||
@ -0,0 +1,71 @@
|
||||
@{
|
||||
ViewData["Title"] = "Lightning Configuration";
|
||||
}
|
||||
@model UpdateSettings<LightningSettings, AdditionalDataStub>;
|
||||
<form class="container" method="post">
|
||||
<input type="hidden" asp-for="Json"/>
|
||||
<div class="row mb-md-4">
|
||||
<h2 class=" text-center w-100">@ViewData["Title"]</h2>
|
||||
</div>
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-4">
|
||||
<label class="card-input-element-label">
|
||||
<input type="radio" asp-for="Settings.Implementation" value="lnd" class="card-input-element d-none"/>
|
||||
<div class="card shadow-sm w-100">
|
||||
<div class="card-img-top bg-light" style="background-image: url(assets/lnd.png); background-repeat: no-repeat ; background-size: contain ;background-position: center; width: 100%; height: 225px"></div>
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-center">LND</h3>
|
||||
<p class="card-text text-center">
|
||||
by Lightning Labs
|
||||
<br/><br/>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="card-input-element-label">
|
||||
<input type="radio" asp-for="Settings.Implementation" value="eclair" class="card-input-element d-none"/>
|
||||
<div class="card shadow-sm w-100">
|
||||
<div class="card-img-top bg-light" style="position:relative; background-image: url(assets/eclair.svg); background-repeat: no-repeat ; background-size: contain ;background-position: center; width: 100%; height: 225px"></div>
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-center">Eclair</h3>
|
||||
<p class="card-text text-center">by ACINQ<br/><br/></p>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="card-input-element-label">
|
||||
<input type="radio" asp-for="Settings.Implementation" value="clightning" class="card-input-element d-none"/>
|
||||
<div class="card dark shadow-sm w-100 ">
|
||||
<div class="card-img-top bg-dark" style="position:relative; background-image: url(assets/clightning.png); background-repeat: no-repeat ; background-size: contain ;background-position: center; width: 100%; height: 225px"></div>
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-center">CLightning</h3>
|
||||
<p class="card-text text-center">By Blockstream<br/><br/></p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-md-4 offset-md-4">
|
||||
<label class="card-input-element-label">
|
||||
<input type="radio" asp-for="Settings.Implementation" value="" class="card-input-element d-none" />
|
||||
<div class="card dark shadow-sm w-100 ">
|
||||
<div class="card-img-top bg-dark" style="position:relative; background-image: url(assets/sad.svg); background-repeat: no-repeat ; background-size: contain ;background-position: center; width: 100%; height: 225px"></div>
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-center">None</h3>
|
||||
<p class="card-text text-center">You're missing out.<br/><br/></p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<span asp-validation-for="Settings.Implementation" class="text-danger w-100 text-center"></span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row w-100">
|
||||
<button type="submit" class="m-auto btn btn-primary btn-lg">Continue</button>
|
||||
</div>
|
||||
</form>
|
||||
256
BTCPayServerDockerConfigurator/Views/Configurator/Summary.cshtml
Normal file
@ -0,0 +1,256 @@
|
||||
@{ ViewData["Title"] = "Does this look right to you?"; }
|
||||
@model UpdateSettings<ConfiguratorSettings, AdditionalDataStub>;
|
||||
<div class="container">
|
||||
<input type="hidden" asp-for="Json" />
|
||||
<div class="row mb-md-4">
|
||||
<h2 class=" text-center w-100">@ViewData["Title"]</h2>
|
||||
</div>
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6 col-sm-12 offset-md-3 ">
|
||||
<div class="card shadow-sm ">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="2">
|
||||
<div class="d-flex justify-content-between">
|
||||
<h3 class="mb-0 pb-0">Deployment</h3>
|
||||
<a asp-action="DeploymentDestination">Edit</a>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tr>
|
||||
<td>Destination</td>
|
||||
<td>@Model.Settings.DeploymentSettings.DeploymentType</td>
|
||||
</tr>
|
||||
@if (Model.Settings.DeploymentSettings.DeploymentType == DeploymentType.RemoteMachine) {
|
||||
<tr>
|
||||
<td>Machine Host</td>
|
||||
<td>@Model.Settings.DeploymentSettings.Host</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Machine User</td>
|
||||
<td>@Model.Settings.DeploymentSettings.Username</td>
|
||||
</tr>
|
||||
}
|
||||
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="2">
|
||||
<div class="d-flex justify-content-between">
|
||||
<h3 class="mb-0 pb-0">Domain</h3>
|
||||
<a asp-action="DomainSettings">Edit</a>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tr>
|
||||
<td>Primary domain</td>
|
||||
<td>@(string.IsNullOrEmpty(Model.Settings.DomainSettings.Domain) ? "Not set" : Model.Settings.DomainSettings.Domain)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Additional domains</td>
|
||||
<td>@(Model.Settings.DomainSettings.AdditionalDomains.Any() ? string.Join(',', Model.Settings.DomainSettings.AdditionalDomains) : "None")</td>
|
||||
</tr>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="2">
|
||||
<div class="d-flex justify-content-between">
|
||||
<h3 class="mb-0 pb-0">Chain</h3>
|
||||
<a asp-action="ChainSettings">Edit</a>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tr>
|
||||
<td>Bitcoin</td>
|
||||
<td>@(Model.Settings.ChainSettings.Bitcoin ? "Enabled" : "Not enabled")</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Network</td>
|
||||
<td>
|
||||
@Model.Settings.ChainSettings.Network.ToString()
|
||||
</td>
|
||||
</tr>
|
||||
@if (Model.Settings.ChainSettings.AltChains.Any()) {
|
||||
<tr>
|
||||
<td>Altcoins</td>
|
||||
<td>@string.Join(',', Model.Settings.ChainSettings.AltChains)</td>
|
||||
</tr>
|
||||
}
|
||||
<tr>
|
||||
<td>Pruning</td>
|
||||
<td>
|
||||
@EnumTextHelper.PruneMode[Model.Settings.ChainSettings.PruneMode]
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="2">
|
||||
<div class="d-flex justify-content-between">
|
||||
<h3 class="mb-0 pb-0">Lightning</h3>
|
||||
<a asp-action="LightningSettings">Edit</a>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tr>
|
||||
<td>Lightning</td>
|
||||
<td>@(string.IsNullOrEmpty(Model.Settings.LightningSettings.Implementation) ? "Not enabled" : Model.Settings.LightningSettings.Implementation)</td>
|
||||
</tr>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="2">
|
||||
<div class="d-flex justify-content-between">
|
||||
<h3 class="mb-0 pb-0">Additional Services</h3>
|
||||
<a asp-action="AdditionalServices">Edit</a>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
BTC Transmuter
|
||||
</td>
|
||||
<td>
|
||||
@(Model.Settings.AdditionalServices.BTCTransmuterSettings.Enabled ? "Enabled" : "Not enabled")
|
||||
</td>
|
||||
</tr>
|
||||
@if (Model.Settings.AdditionalServices.BTCTransmuterSettings.Enabled) {
|
||||
<tr>
|
||||
<td>
|
||||
BTC Transmuter Host
|
||||
</td>
|
||||
<td>
|
||||
@Model.Settings.AdditionalServices.BTCTransmuterSettings.Host
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
<tr>
|
||||
<td>
|
||||
Libre Patron
|
||||
</td>
|
||||
<td>
|
||||
@(Model.Settings.AdditionalServices.LibrePatronSettings.Enabled ? "Enabled" : "Not enabled")
|
||||
</td>
|
||||
</tr>
|
||||
@if (Model.Settings.AdditionalServices.LibrePatronSettings.Enabled) {
|
||||
<tr>
|
||||
<td>
|
||||
Libre Patron Host
|
||||
</td>
|
||||
<td>
|
||||
@Model.Settings.AdditionalServices.LibrePatronSettings.Host
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
<tr>
|
||||
<td>
|
||||
WooCommerce
|
||||
</td>
|
||||
<td>
|
||||
@(Model.Settings.AdditionalServices.WooCommerceSettings.Enabled ? "Enabled" : "Not enabled")
|
||||
</td>
|
||||
</tr>
|
||||
@if (Model.Settings.AdditionalServices.WooCommerceSettings.Enabled) {
|
||||
<tr>
|
||||
<td>
|
||||
WooCommerce Host
|
||||
</td>
|
||||
<td>
|
||||
@Model.Settings.AdditionalServices.WooCommerceSettings.Host
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
<tr>
|
||||
<td>
|
||||
Tor Relay
|
||||
</td>
|
||||
<td>
|
||||
@(Model.Settings.AdditionalServices.TorRelaySettings.Enabled ? "Enabled" : "Not enabled")
|
||||
</td>
|
||||
</tr>
|
||||
@if (Model.Settings.AdditionalServices.TorRelaySettings.Enabled) {
|
||||
<tr>
|
||||
<td>
|
||||
Tor Relay nickname
|
||||
</td>
|
||||
<td>
|
||||
@Model.Settings.AdditionalServices.TorRelaySettings.Nickname
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
Tor Relay contact email
|
||||
</td>
|
||||
<td>
|
||||
@Model.Settings.AdditionalServices.TorRelaySettings.Email
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="2">
|
||||
<div class="d-flex justify-content-between">
|
||||
<h3 class="mb-0 pb-0">Advanced</h3>
|
||||
<a asp-action="AdvancedSettings">Edit</a>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@if (Model.Settings.AdvancedSettings.AnythingSet())
|
||||
{
|
||||
<tr>
|
||||
<td>Custom BTCPay Image</td>
|
||||
<td>@(string.IsNullOrEmpty(Model.Settings.AdvancedSettings.CustomBTCPayImage) ? "Not set" : Model.Settings.AdvancedSettings.CustomBTCPayImage)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>BTCPay docker Git repository</td>
|
||||
<td>@(string.IsNullOrEmpty(Model.Settings.AdvancedSettings.BTCPayDockerRepository) ? "Not set" : Model.Settings.AdvancedSettings.BTCPayDockerRepository)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>BTCPay docker Git branch</td>
|
||||
<td>@(string.IsNullOrEmpty(Model.Settings.AdvancedSettings.BTCPayDockerBranch) ? "Not set" : Model.Settings.AdvancedSettings.BTCPayDockerBranch)</td>
|
||||
</tr>
|
||||
@if (Model.Settings.AdvancedSettings.AdditionalFragments.Any())
|
||||
{
|
||||
<tr>
|
||||
<td>Additional fragments</td>
|
||||
<td>@string.Join(',', Model.Settings.AdvancedSettings.AdditionalFragments)</td>
|
||||
</tr>
|
||||
}
|
||||
@if (Model.Settings.AdvancedSettings.ExcludedFragments.Any())
|
||||
{
|
||||
<tr>
|
||||
<td>Excluded fragments</td>
|
||||
<td>@string.Join(',', Model.Settings.AdvancedSettings.ExcludedFragments)</td>
|
||||
</tr>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<tr><td colspan="2">No advanced settings configured</td></tr>
|
||||
}
|
||||
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="post" asp-action="Deploy" class="row w-100">
|
||||
<input type="hidden" asp-for="Json" />
|
||||
|
||||
<button type="submit" class="m-auto btn btn-primary btn-lg p-0 border-0 overflow-hidden justdoitbutton bg-transparent">
|
||||
<img src="assets/doit.gif" height="100" />
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
25
BTCPayServerDockerConfigurator/Views/Shared/Error.cshtml
Normal file
@ -0,0 +1,25 @@
|
||||
@model ErrorViewModel
|
||||
@{
|
||||
ViewData["Title"] = "Error";
|
||||
}
|
||||
|
||||
<h1 class="text-danger">Error.</h1>
|
||||
<h2 class="text-danger">An error occurred while processing your request.</h2>
|
||||
|
||||
@if (Model.ShowRequestId)
|
||||
{
|
||||
<p>
|
||||
<strong>Request ID:</strong> <code>@Model.RequestId</code>
|
||||
</p>
|
||||
}
|
||||
|
||||
<h3>Development Mode</h3>
|
||||
<p>
|
||||
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
|
||||
</p>
|
||||
<p>
|
||||
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
|
||||
It can result in displaying sensitive information from exceptions to end users.
|
||||
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
|
||||
and restarting the app.
|
||||
</p>
|
||||
29
BTCPayServerDockerConfigurator/Views/Shared/_Layout.cshtml
Normal file
@ -0,0 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>@ViewData["Title"] - BTCPayServer Docker Configurator</title>
|
||||
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css"/>
|
||||
<link rel="stylesheet" href="~/css/site.css"/>
|
||||
</head>
|
||||
<body class="bg-light">
|
||||
<header class="bg-dark mb-md-4">
|
||||
<div class="container p-md-5 text-white">
|
||||
<h1 class="text-center w-100">BTCPay Server Configurator</h1>
|
||||
</div>
|
||||
</header>
|
||||
<div class="container">
|
||||
<main role="main" class="pb-3">
|
||||
@RenderBody()
|
||||
</main>
|
||||
</div>
|
||||
<script src="~/lib/jquery/dist/jquery.min.js"></script>
|
||||
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
|
||||
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
|
||||
<script src="~/js/site.js" asp-append-version="true"></script>
|
||||
@RenderSection("Scripts", required: false)
|
||||
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,2 @@
|
||||
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
|
||||
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
|
||||
3
BTCPayServerDockerConfigurator/Views/_ViewImports.cshtml
Normal file
@ -0,0 +1,3 @@
|
||||
@using BTCPayServerDockerConfigurator
|
||||
@using BTCPayServerDockerConfigurator.Models
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
3
BTCPayServerDockerConfigurator/Views/_ViewStart.cshtml
Normal file
@ -0,0 +1,3 @@
|
||||
@{
|
||||
Layout = "_Layout";
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Debug",
|
||||
"System": "Information",
|
||||
"Microsoft": "Information"
|
||||
}
|
||||
}
|
||||
}
|
||||
10
BTCPayServerDockerConfigurator/appsettings.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="64" width="64" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<g transform="translate(0.00630876,-0.00301984)">
|
||||
<path fill="#f7931a" d="m63.033,39.744c-4.274,17.143-21.637,27.576-38.782,23.301-17.138-4.274-27.571-21.638-23.295-38.78,4.272-17.145,21.635-27.579,38.775-23.305,17.144,4.274,27.576,21.64,23.302,38.784z"/>
|
||||
<path fill="#FFF" d="m46.103,27.444c0.637-4.258-2.605-6.547-7.038-8.074l1.438-5.768-3.511-0.875-1.4,5.616c-0.923-0.23-1.871-0.447-2.813-0.662l1.41-5.653-3.509-0.875-1.439,5.766c-0.764-0.174-1.514-0.346-2.242-0.527l0.004-0.018-4.842-1.209-0.934,3.75s2.605,0.597,2.55,0.634c1.422,0.355,1.679,1.296,1.636,2.042l-1.638,6.571c0.098,0.025,0.225,0.061,0.365,0.117-0.117-0.029-0.242-0.061-0.371-0.092l-2.296,9.205c-0.174,0.432-0.615,1.08-1.609,0.834,0.035,0.051-2.552-0.637-2.552-0.637l-1.743,4.019,4.569,1.139c0.85,0.213,1.683,0.436,2.503,0.646l-1.453,5.834,3.507,0.875,1.439-5.772c0.958,0.26,1.888,0.5,2.798,0.726l-1.434,5.745,3.511,0.875,1.453-5.823c5.987,1.133,10.489,0.676,12.384-4.739,1.527-4.36-0.076-6.875-3.226-8.515,2.294-0.529,4.022-2.038,4.483-5.155zm-8.022,11.249c-1.085,4.36-8.426,2.003-10.806,1.412l1.928-7.729c2.38,0.594,10.012,1.77,8.878,6.317zm1.086-11.312c-0.99,3.966-7.1,1.951-9.082,1.457l1.748-7.01c1.982,0.494,8.365,1.416,7.334,5.553z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
BIN
BTCPayServerDockerConfigurator/wwwroot/assets/clightning.png
Normal file
|
After Width: | Height: | Size: 83 KiB |
BIN
BTCPayServerDockerConfigurator/wwwroot/assets/doit.gif
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
BTCPayServerDockerConfigurator/wwwroot/assets/eclair.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
1
BTCPayServerDockerConfigurator/wwwroot/assets/eclair.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg id="logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 186 106" shape-rendering="crispEdges"><defs><style>.cls-1{fill:#a3d6ff;}.cls-2{fill:#3db0ff;}.cls-3{fill:#b5d9f7;}.cls-4{fill:#75c4ef;}.cls-5{fill:#a4eaff;}.cls-6{fill:#8ec5f5;}.cls-7{fill:#b5dcff;}.cls-8{fill:#1a4184;}.cls-9{fill:#202963;}.cls-10{fill:#315e9e;}.cls-11{fill:#2c005e;}.cls-12{fill:#4786ba;}.cls-13{fill:#4991cc;}.cls-14{fill:#3f77a6;}.cls-15{fill:#9e0567;}.cls-16{fill:#163372;}.cls-17{fill:#0a103a;}.cls-18{fill:#002270;}.cls-19{fill:#150056;}.cls-20{fill:#1b6294;}.cls-21{fill:#0064bd;}.cls-22{fill:#306b94;}.cls-23{fill:#1e5ba6;}.cls-24{fill:#00143c;}.cls-25{fill:#224c69;}.cls-26{fill:#163c70;}.cls-27{fill:#1c5084;}.cls-28{fill:#371863;}.cls-29{fill:#ce84bf;}.cls-30{fill:#e2acd4;}.cls-31{fill:#872983;}.cls-32{fill:#69d0e8;}.cls-33{fill:#2e90c6;}.cls-34{fill:#efc2e6;}.cls-35{fill:#c163a9;}</style></defs><title>eclair-logo</title><g id="logo-2" data-name="logo"><polygon class="cls-1" points="64 0 56 17 64 24 64 0"/><polygon class="cls-2" points="40 39 64 24 56 17 40 39"/><polygon class="cls-3" points="30 34 56 17 64 0 30 34"/><polygon class="cls-4" points="56 17 30 34 40 39 56 17"/><polygon class="cls-5" points="0 64 40 39 30 34 0 64"/><polygon class="cls-6" points="64 0 64 24 80 27 64 0"/><polygon class="cls-7" points="64 0 80 27 94 30 64 0"/><polygon class="cls-8" points="112 72 107 43 89 49 112 72"/><polygon class="cls-9" points="122 58 107 43 112 72 122 58"/><polygon class="cls-10" points="107 43 94 30 89 49 107 43"/><polygon class="cls-11" points="122 82 122 58 112 72 122 82"/><polygon class="cls-12" points="64 24 77 37 80 27 64 24"/><polygon class="cls-13" points="77 37 89 49 80 27 77 37"/><polygon class="cls-14" points="89 49 94 30 80 27 89 49"/><polygon class="cls-15" points="149 65 122 58 122 82 149 65"/><polygon class="cls-16" points="100 71 102 86 112 84 100 71"/><polygon class="cls-17" points="102 86 122 106 112 84 102 86"/><polygon class="cls-18" points="84 68 102 86 100 71 84 68"/><polygon class="cls-19" points="112 84 122 106 122 82 112 84"/><polygon class="cls-20" points="77 49 89 49 77 37 77 49"/><polygon class="cls-21" points="64 48 84 68 77 49 64 48"/><polygon class="cls-22" points="64 24 64 48 77 37 64 24"/><polygon class="cls-23" points="89 49 100 71 112 72 89 49"/><polygon class="cls-24" points="100 71 112 84 112 72 100 71"/><polygon class="cls-25" points="77 37 64 48 77 49 77 37"/><polygon class="cls-26" points="89 49 84 68 100 71 89 49"/><polygon class="cls-27" points="77 49 84 68 89 49 77 49"/><polygon class="cls-28" points="112 84 122 82 112 72 112 84"/><polygon class="cls-29" points="122 106 157 71 149 65 122 106"/><polygon class="cls-30" points="149 65 157 71 186 42 149 65"/><polygon class="cls-31" points="122 82 122 106 149 65 122 82"/><polygon class="cls-32" points="0 64 64 48 40 39 0 64"/><polygon class="cls-33" points="40 39 64 48 64 24 40 39"/><polygon class="cls-34" points="146 52 149 65 186 42 146 52"/><polygon class="cls-35" points="146 52 122 58 149 65 146 52"/></g></svg>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
BIN
BTCPayServerDockerConfigurator/wwwroot/assets/lnd.png
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1000 1000" enable-background="new 0 0 1000 1000" xml:space="preserve">
|
||||
<g><path fill="#3E474A" d="M207.5,10c-12.4,0-22.3,10-22.3,22.3v935.3c0,12.4,10,22.4,22.3,22.4h584.9c12.4,0,22.4-10,22.4-22.4V32.3c0-12.4-10-22.3-22.4-22.3H207.5L207.5,10z M256.3,118.9h487.5v139.7H256.3V118.9L256.3,118.9z M256.3,293.3h487.5V433H256.3V293.3z M256.3,464.9h487.5v139.7H256.3V464.9L256.3,464.9z M500,834.8c28.7,0,52,23.3,52,52.1s-23.3,52-52,52c-28.7,0-52.1-23.3-52.1-52S471.3,834.8,500,834.8L500,834.8z"/></g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 764 B |
5
BTCPayServerDockerConfigurator/wwwroot/assets/remote.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1000 1000" enable-background="new 0 0 1000 1000" xml:space="preserve">
|
||||
<g><g fill="#3E474A" transform="translate(0.000000,511.000000) scale(0.100000,-0.100000)"><path d="M6998.9,4985.3c-214.9-30.4-499.9-114.5-584-170.5c-151.8-102.8-168.2-264-32.7-378.4c84.1-72.4,123.8-70.1,471.9,23.4c934.4,250,1931.9-210.2,2359.4-1090.9c144.9-299,198.6-542,198.6-894.7c0-233.6-11.7-324.7-60.7-513.9c-95.8-355.1-95.8-387.8-14-478.9c58.4-65.4,86.4-79.4,168.2-79.4c133.2,0,214.9,93.4,282.6,320c184.6,621.4,140.2,1245.1-128.5,1831.4C9199.5,4550.8,8094.5,5146.5,6998.9,4985.3z"/><path d="M7122.7,4074.3c-200.9-35-357.4-91.1-425.1-156.5c-130.8-119.1-65.4-348.1,109.8-392.5c37.4-9.3,109.8,0,182.2,25.7c158.8,53.7,441.5,70.1,602.7,37.4c436.9-91.1,796.6-450.9,885.4-883c35-165.8,21-420.5-35-593.4c-49-151.8-35-231.3,46.7-315.4c77.1-77.1,217.3-77.1,308.4,0c130.8,109.8,214.9,539.6,170.5,869c-51.4,390.1-193.9,675.1-478.9,957.8c-205.5,205.6-455.5,350.4-719.5,415.8C7589.9,4085.9,7281.6,4102.3,7122.7,4074.3z"/><path d="M7132.1,3132.8c-306-137.8-165.8-532.6,158.9-450.9c191.5,49.1,329.4-95.8,280.3-289.7c-44.4-170.5,35-294.3,200.9-317.7c72.4-9.3,102.8,0,161.2,49.1c98.1,84.1,130.8,193.9,119.1,397.1c-16.4,259.3-147.2,460.2-376.1,579.3C7552.5,3163.2,7241.8,3184.2,7132.1,3132.8z"/><path d="M5840.2,3023c-250-49.1-196.2,0-2962.1-2763.5C-100.3-2714.3,102.9-2487.7,102.9-2849.7c0-327.1,23.4-359.8,801.3-1137.7c780.2-777.9,812.9-801.3,1137.7-801.3c362.1,0,135.5-200.9,3109.3,2775.2C7171.8,7.2,7804.9,656.6,7842.2,736.1c109.8,235.9,109.8,469.5,0,700.8c-35,72.4-243,299-682.1,742.9c-348,352.7-677.5,670.4-731.2,707.8C6251.4,3011.4,6031.8,3060.4,5840.2,3023z M6153.3,2016.2c254.6-63.1,530.3-282.7,661.1-527.9c95.8-179.9,123.8-301.4,123.8-516.3c0-532.6-364.4-960.1-894.7-1055.9c-329.4-58.4-682.1,56.1-927.4,301.3c-373.8,371.4-429.8,925.1-137.8,1359.6C5232.9,1960.1,5690.7,2130.7,6153.3,2016.2z M4244.7,453.4c264-128.5,310.7-497.6,86.4-693.8c-345.7-303.7-852.7,56.1-677.5,478.9c49.1,116.8,98.1,168.2,210.2,219.6C3987.7,516.5,4118.6,514.1,4244.7,453.4z M5207.2-488c243-161.2,243-544.3,2.4-705.5c-271-177.5-619.1-28-670.5,289.7c-28,182.2,102.8,401.8,282.7,469.5C4926.8-396.9,5106.7-422.6,5207.2-488z"/><path d="M5728.1,1579.4c-299-77.1-495.2-352.7-467.2-654.1c35-397.1,427.5-654.1,801.3-530.3c429.8,142.5,560.6,696.1,240.6,1016.2C6162.6,1553.7,5910.3,1626.1,5728.1,1579.4z"/></g></g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
46
BTCPayServerDockerConfigurator/wwwroot/assets/sad.svg
Normal file
@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 121.669 121.669" style="enable-background:new 0 0 121.669 121.669;" xml:space="preserve">
|
||||
<g>
|
||||
<path fill="#fff" d="M60.834,121.669C27.291,121.669,0,94.379,0,60.834C0,27.29,27.291,0,60.834,0s60.834,27.29,60.834,60.834
|
||||
C121.669,94.379,94.378,121.669,60.834,121.669z M60.834,6C30.599,6,6,30.599,6,60.834c0,30.236,24.599,54.835,54.834,54.835
|
||||
s54.834-24.599,54.834-54.835C115.669,30.599,91.07,6,60.834,6z"/>
|
||||
<path fill="#fff" d="M78.843,97.437h-6c0-5.642-5.477-10.231-12.208-10.231s-12.208,4.59-12.208,10.231h-6c0-8.95,8.168-16.231,18.208-16.231
|
||||
S78.843,88.486,78.843,97.437z"/>
|
||||
<path fill="#fff" d="M32.14,69.218c-7.853,0-14.242-7.115-14.242-15.86h6c0,5.437,3.697,9.86,8.242,9.86s8.242-4.423,8.242-9.86h6
|
||||
C46.382,62.103,39.993,69.218,32.14,69.218z"/>
|
||||
<path fill="#fff" d="M89.529,69.218c-7.854,0-14.242-7.115-14.242-15.86h6c0,5.437,3.697,9.86,8.242,9.86s8.242-4.423,8.242-9.86h6
|
||||
C103.771,62.103,97.382,69.218,89.529,69.218z"/>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
BIN
BTCPayServerDockerConfigurator/wwwroot/assets/shell.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
162
BTCPayServerDockerConfigurator/wwwroot/css/site.css
Normal file
@ -0,0 +1,162 @@
|
||||
/* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification
|
||||
for details on configuring this project to bundle and minify static web assets. */
|
||||
|
||||
a.navbar-brand {
|
||||
white-space: normal;
|
||||
text-align: center;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
/* Provide sufficient contrast against white background */
|
||||
a {
|
||||
color: #0366d6;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
color: #fff;
|
||||
background-color: #1b6ec2;
|
||||
border-color: #1861ac;
|
||||
}
|
||||
|
||||
.nav-pills .nav-link.active, .nav-pills .show > .nav-link {
|
||||
color: #fff;
|
||||
background-color: #1b6ec2;
|
||||
border-color: #1861ac;
|
||||
}
|
||||
|
||||
/* Sticky footer styles
|
||||
-------------------------------------------------- */
|
||||
html {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
html {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.border-top {
|
||||
border-top: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
.border-bottom {
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
.box-shadow {
|
||||
box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
|
||||
}
|
||||
|
||||
button.accept-policy {
|
||||
font-size: 1rem;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
/* Sticky footer styles
|
||||
-------------------------------------------------- */
|
||||
html {
|
||||
position: relative;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
/* Margin bottom by footer height */
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
line-height: 60px; /* Vertically center the text there */
|
||||
}
|
||||
|
||||
|
||||
/*cards as an input element*/
|
||||
|
||||
label.card-input-element-label {
|
||||
width: 100%;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.card-input-element + .card {
|
||||
border: 2px solid transparent;
|
||||
display: inline-table;
|
||||
}
|
||||
|
||||
.card-input-element + .card:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.card-input-element:checked + .card {
|
||||
border: 2px solid var(--secondary);
|
||||
-webkit-transition: border .3s;
|
||||
-o-transition: border .3s;
|
||||
transition: border .3s;
|
||||
}
|
||||
|
||||
label{
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
.card-input-element:checked+.card::after {
|
||||
content: '✓';
|
||||
color: #1b1e21;
|
||||
|
||||
font-size: 50px;
|
||||
-webkit-animation-name: fadeInCheckbox;
|
||||
animation-name: fadeInCheckbox;
|
||||
-webkit-animation-duration: .5s;
|
||||
animation-duration: .5s;
|
||||
-webkit-animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top:0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.card-input-element:checked+.card.dark::after{
|
||||
color: var(--light);
|
||||
}
|
||||
|
||||
@-webkit-keyframes fadeInCheckbox {
|
||||
from {
|
||||
opacity: 0;
|
||||
-webkit-transform: rotateZ(-20deg);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
-webkit-transform: rotateZ(0deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInCheckbox {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: rotateZ(-20deg);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: rotateZ(0deg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.styled-checkbox-label:hover{
|
||||
background-color: var(--light);
|
||||
}
|
||||
|
||||
|
||||
.justdoitbutton:hover{
|
||||
-webkit-filter: brightness(70%);
|
||||
-webkit-transition: all 1s ease;
|
||||
-moz-transition: all 1s ease;
|
||||
-o-transition: all 1s ease;
|
||||
-ms-transition: all 1s ease;
|
||||
transition: all 1s ease;
|
||||
}
|
||||
/**/
|
||||
BIN
BTCPayServerDockerConfigurator/wwwroot/favicon.ico
Normal file
|
After Width: | Height: | Size: 31 KiB |
4
BTCPayServerDockerConfigurator/wwwroot/js/site.js
Normal file
@ -0,0 +1,4 @@
|
||||
// Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification
|
||||
// for details on configuring this project to bundle and minify static web assets.
|
||||
|
||||
// Write your JavaScript code.
|
||||
22
BTCPayServerDockerConfigurator/wwwroot/lib/bootstrap/LICENSE
Normal file
@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2011-2018 Twitter, Inc.
|
||||
Copyright (c) 2011-2018 The Bootstrap Authors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
3719
BTCPayServerDockerConfigurator/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css
vendored
Normal file
1
BTCPayServerDockerConfigurator/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css.map
vendored
Normal file
7
BTCPayServerDockerConfigurator/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css
vendored
Normal file
1
BTCPayServerDockerConfigurator/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css.map
vendored
Normal file
331
BTCPayServerDockerConfigurator/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css
vendored
Normal file
@ -0,0 +1,331 @@
|
||||
/*!
|
||||
* Bootstrap Reboot v4.3.1 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2019 The Bootstrap Authors
|
||||
* Copyright 2011-2019 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
|
||||
*/
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
font-family: sans-serif;
|
||||
line-height: 1.15;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
article, aside, figcaption, figure, footer, header, hgroup, main, nav, section {
|
||||
display: block;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
color: #212529;
|
||||
text-align: left;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
[tabindex="-1"]:focus {
|
||||
outline: 0 !important;
|
||||
}
|
||||
|
||||
hr {
|
||||
box-sizing: content-box;
|
||||
height: 0;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
abbr[title],
|
||||
abbr[data-original-title] {
|
||||
text-decoration: underline;
|
||||
-webkit-text-decoration: underline dotted;
|
||||
text-decoration: underline dotted;
|
||||
cursor: help;
|
||||
border-bottom: 0;
|
||||
-webkit-text-decoration-skip-ink: none;
|
||||
text-decoration-skip-ink: none;
|
||||
}
|
||||
|
||||
address {
|
||||
margin-bottom: 1rem;
|
||||
font-style: normal;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul,
|
||||
dl {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
ol ol,
|
||||
ul ul,
|
||||
ol ul,
|
||||
ul ol {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-bottom: .5rem;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
sub,
|
||||
sup {
|
||||
position: relative;
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -.5em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #0056b3;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:not([href]):not([tabindex]) {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:not([href]):not([tabindex]):focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
pre,
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
figure {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
img {
|
||||
vertical-align: middle;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
svg {
|
||||
overflow: hidden;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
caption {
|
||||
padding-top: 0.75rem;
|
||||
padding-bottom: 0.75rem;
|
||||
color: #6c757d;
|
||||
text-align: left;
|
||||
caption-side: bottom;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: inherit;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
button:focus {
|
||||
outline: 1px dotted;
|
||||
outline: 5px auto -webkit-focus-ring-color;
|
||||
}
|
||||
|
||||
input,
|
||||
button,
|
||||
select,
|
||||
optgroup,
|
||||
textarea {
|
||||
margin: 0;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
button,
|
||||
input {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
button,
|
||||
select {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
select {
|
||||
word-wrap: normal;
|
||||
}
|
||||
|
||||
button,
|
||||
[type="button"],
|
||||
[type="reset"],
|
||||
[type="submit"] {
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
button:not(:disabled),
|
||||
[type="button"]:not(:disabled),
|
||||
[type="reset"]:not(:disabled),
|
||||
[type="submit"]:not(:disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button::-moz-focus-inner,
|
||||
[type="button"]::-moz-focus-inner,
|
||||
[type="reset"]::-moz-focus-inner,
|
||||
[type="submit"]::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
input[type="radio"],
|
||||
input[type="checkbox"] {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
input[type="date"],
|
||||
input[type="time"],
|
||||
input[type="datetime-local"],
|
||||
input[type="month"] {
|
||||
-webkit-appearance: listbox;
|
||||
}
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
min-width: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
legend {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
padding: 0;
|
||||
margin-bottom: .5rem;
|
||||
font-size: 1.5rem;
|
||||
line-height: inherit;
|
||||
color: inherit;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
[type="number"]::-webkit-inner-spin-button,
|
||||
[type="number"]::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
[type="search"] {
|
||||
outline-offset: -2px;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
font: inherit;
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
output {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
/*# sourceMappingURL=bootstrap-reboot.css.map */
|
||||
1
BTCPayServerDockerConfigurator/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css.map
vendored
Normal file
8
BTCPayServerDockerConfigurator/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
/*!
|
||||
* Bootstrap Reboot v4.3.1 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2019 The Bootstrap Authors
|
||||
* Copyright 2011-2019 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
|
||||
*/*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}
|
||||
/*# sourceMappingURL=bootstrap-reboot.min.css.map */
|
||||
1
BTCPayServerDockerConfigurator/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map
vendored
Normal file
10038
BTCPayServerDockerConfigurator/wwwroot/lib/bootstrap/dist/css/bootstrap.css
vendored
Normal file
1
BTCPayServerDockerConfigurator/wwwroot/lib/bootstrap/dist/css/bootstrap.css.map
vendored
Normal file
7
BTCPayServerDockerConfigurator/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css
vendored
Normal file
1
BTCPayServerDockerConfigurator/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css.map
vendored
Normal file
7013
BTCPayServerDockerConfigurator/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js
vendored
Normal file
1
BTCPayServerDockerConfigurator/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js.map
vendored
Normal file
7
BTCPayServerDockerConfigurator/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js
vendored
Normal file
1
BTCPayServerDockerConfigurator/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js.map
vendored
Normal file
4435
BTCPayServerDockerConfigurator/wwwroot/lib/bootstrap/dist/js/bootstrap.js
vendored
Normal file
1
BTCPayServerDockerConfigurator/wwwroot/lib/bootstrap/dist/js/bootstrap.js.map
vendored
Normal file
7
BTCPayServerDockerConfigurator/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js
vendored
Normal file
1
BTCPayServerDockerConfigurator/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js.map
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
Copyright (c) .NET Foundation. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
these files except in compliance with the License. You may obtain a copy of the
|
||||
License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software distributed
|
||||
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations under the License.
|
||||
@ -0,0 +1,432 @@
|
||||
// Unobtrusive validation support library for jQuery and jQuery Validate
|
||||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
// @version v3.2.11
|
||||
|
||||
/*jslint white: true, browser: true, onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: true, newcap: true, immed: true, strict: false */
|
||||
/*global document: false, jQuery: false */
|
||||
|
||||
(function (factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define("jquery.validate.unobtrusive", ['jquery-validation'], factory);
|
||||
} else if (typeof module === 'object' && module.exports) {
|
||||
// CommonJS-like environments that support module.exports
|
||||
module.exports = factory(require('jquery-validation'));
|
||||
} else {
|
||||
// Browser global
|
||||
jQuery.validator.unobtrusive = factory(jQuery);
|
||||
}
|
||||
}(function ($) {
|
||||
var $jQval = $.validator,
|
||||
adapters,
|
||||
data_validation = "unobtrusiveValidation";
|
||||
|
||||
function setValidationValues(options, ruleName, value) {
|
||||
options.rules[ruleName] = value;
|
||||
if (options.message) {
|
||||
options.messages[ruleName] = options.message;
|
||||
}
|
||||
}
|
||||
|
||||
function splitAndTrim(value) {
|
||||
return value.replace(/^\s+|\s+$/g, "").split(/\s*,\s*/g);
|
||||
}
|
||||
|
||||
function escapeAttributeValue(value) {
|
||||
// As mentioned on http://api.jquery.com/category/selectors/
|
||||
return value.replace(/([!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~])/g, "\\$1");
|
||||
}
|
||||
|
||||
function getModelPrefix(fieldName) {
|
||||
return fieldName.substr(0, fieldName.lastIndexOf(".") + 1);
|
||||
}
|
||||
|
||||
function appendModelPrefix(value, prefix) {
|
||||
if (value.indexOf("*.") === 0) {
|
||||
value = value.replace("*.", prefix);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function onError(error, inputElement) { // 'this' is the form element
|
||||
var container = $(this).find("[data-valmsg-for='" + escapeAttributeValue(inputElement[0].name) + "']"),
|
||||
replaceAttrValue = container.attr("data-valmsg-replace"),
|
||||
replace = replaceAttrValue ? $.parseJSON(replaceAttrValue) !== false : null;
|
||||
|
||||
container.removeClass("field-validation-valid").addClass("field-validation-error");
|
||||
error.data("unobtrusiveContainer", container);
|
||||
|
||||
if (replace) {
|
||||
container.empty();
|
||||
error.removeClass("input-validation-error").appendTo(container);
|
||||
}
|
||||
else {
|
||||
error.hide();
|
||||
}
|
||||
}
|
||||
|
||||
function onErrors(event, validator) { // 'this' is the form element
|
||||
var container = $(this).find("[data-valmsg-summary=true]"),
|
||||
list = container.find("ul");
|
||||
|
||||
if (list && list.length && validator.errorList.length) {
|
||||
list.empty();
|
||||
container.addClass("validation-summary-errors").removeClass("validation-summary-valid");
|
||||
|
||||
$.each(validator.errorList, function () {
|
||||
$("<li />").html(this.message).appendTo(list);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function onSuccess(error) { // 'this' is the form element
|
||||
var container = error.data("unobtrusiveContainer");
|
||||
|
||||
if (container) {
|
||||
var replaceAttrValue = container.attr("data-valmsg-replace"),
|
||||
replace = replaceAttrValue ? $.parseJSON(replaceAttrValue) : null;
|
||||
|
||||
container.addClass("field-validation-valid").removeClass("field-validation-error");
|
||||
error.removeData("unobtrusiveContainer");
|
||||
|
||||
if (replace) {
|
||||
container.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onReset(event) { // 'this' is the form element
|
||||
var $form = $(this),
|
||||
key = '__jquery_unobtrusive_validation_form_reset';
|
||||
if ($form.data(key)) {
|
||||
return;
|
||||
}
|
||||
// Set a flag that indicates we're currently resetting the form.
|
||||
$form.data(key, true);
|
||||
try {
|
||||
$form.data("validator").resetForm();
|
||||
} finally {
|
||||
$form.removeData(key);
|
||||
}
|
||||
|
||||
$form.find(".validation-summary-errors")
|
||||
.addClass("validation-summary-valid")
|
||||
.removeClass("validation-summary-errors");
|
||||
$form.find(".field-validation-error")
|
||||
.addClass("field-validation-valid")
|
||||
.removeClass("field-validation-error")
|
||||
.removeData("unobtrusiveContainer")
|
||||
.find(">*") // If we were using valmsg-replace, get the underlying error
|
||||
.removeData("unobtrusiveContainer");
|
||||
}
|
||||
|
||||
function validationInfo(form) {
|
||||
var $form = $(form),
|
||||
result = $form.data(data_validation),
|
||||
onResetProxy = $.proxy(onReset, form),
|
||||
defaultOptions = $jQval.unobtrusive.options || {},
|
||||
execInContext = function (name, args) {
|
||||
var func = defaultOptions[name];
|
||||
func && $.isFunction(func) && func.apply(form, args);
|
||||
};
|
||||
|
||||
if (!result) {
|
||||
result = {
|
||||
options: { // options structure passed to jQuery Validate's validate() method
|
||||
errorClass: defaultOptions.errorClass || "input-validation-error",
|
||||
errorElement: defaultOptions.errorElement || "span",
|
||||
errorPlacement: function () {
|
||||
onError.apply(form, arguments);
|
||||
execInContext("errorPlacement", arguments);
|
||||
},
|
||||
invalidHandler: function () {
|
||||
onErrors.apply(form, arguments);
|
||||
execInContext("invalidHandler", arguments);
|
||||
},
|
||||
messages: {},
|
||||
rules: {},
|
||||
success: function () {
|
||||
onSuccess.apply(form, arguments);
|
||||
execInContext("success", arguments);
|
||||
}
|
||||
},
|
||||
attachValidation: function () {
|
||||
$form
|
||||
.off("reset." + data_validation, onResetProxy)
|
||||
.on("reset." + data_validation, onResetProxy)
|
||||
.validate(this.options);
|
||||
},
|
||||
validate: function () { // a validation function that is called by unobtrusive Ajax
|
||||
$form.validate();
|
||||
return $form.valid();
|
||||
}
|
||||
};
|
||||
$form.data(data_validation, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
$jQval.unobtrusive = {
|
||||
adapters: [],
|
||||
|
||||
parseElement: function (element, skipAttach) {
|
||||
/// <summary>
|
||||
/// Parses a single HTML element for unobtrusive validation attributes.
|
||||
/// </summary>
|
||||
/// <param name="element" domElement="true">The HTML element to be parsed.</param>
|
||||
/// <param name="skipAttach" type="Boolean">[Optional] true to skip attaching the
|
||||
/// validation to the form. If parsing just this single element, you should specify true.
|
||||
/// If parsing several elements, you should specify false, and manually attach the validation
|
||||
/// to the form when you are finished. The default is false.</param>
|
||||
var $element = $(element),
|
||||
form = $element.parents("form")[0],
|
||||
valInfo, rules, messages;
|
||||
|
||||
if (!form) { // Cannot do client-side validation without a form
|
||||
return;
|
||||
}
|
||||
|
||||
valInfo = validationInfo(form);
|
||||
valInfo.options.rules[element.name] = rules = {};
|
||||
valInfo.options.messages[element.name] = messages = {};
|
||||
|
||||
$.each(this.adapters, function () {
|
||||
var prefix = "data-val-" + this.name,
|
||||
message = $element.attr(prefix),
|
||||
paramValues = {};
|
||||
|
||||
if (message !== undefined) { // Compare against undefined, because an empty message is legal (and falsy)
|
||||
prefix += "-";
|
||||
|
||||
$.each(this.params, function () {
|
||||
paramValues[this] = $element.attr(prefix + this);
|
||||
});
|
||||
|
||||
this.adapt({
|
||||
element: element,
|
||||
form: form,
|
||||
message: message,
|
||||
params: paramValues,
|
||||
rules: rules,
|
||||
messages: messages
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$.extend(rules, { "__dummy__": true });
|
||||
|
||||
if (!skipAttach) {
|
||||
valInfo.attachValidation();
|
||||
}
|
||||
},
|
||||
|
||||
parse: function (selector) {
|
||||
/// <summary>
|
||||
/// Parses all the HTML elements in the specified selector. It looks for input elements decorated
|
||||
/// with the [data-val=true] attribute value and enables validation according to the data-val-*
|
||||
/// attribute values.
|
||||
/// </summary>
|
||||
/// <param name="selector" type="String">Any valid jQuery selector.</param>
|
||||
|
||||
// $forms includes all forms in selector's DOM hierarchy (parent, children and self) that have at least one
|
||||
// element with data-val=true
|
||||
var $selector = $(selector),
|
||||
$forms = $selector.parents()
|
||||
.addBack()
|
||||
.filter("form")
|
||||
.add($selector.find("form"))
|
||||
.has("[data-val=true]");
|
||||
|
||||
$selector.find("[data-val=true]").each(function () {
|
||||
$jQval.unobtrusive.parseElement(this, true);
|
||||
});
|
||||
|
||||
$forms.each(function () {
|
||||
var info = validationInfo(this);
|
||||
if (info) {
|
||||
info.attachValidation();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
adapters = $jQval.unobtrusive.adapters;
|
||||
|
||||
adapters.add = function (adapterName, params, fn) {
|
||||
/// <summary>Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation.</summary>
|
||||
/// <param name="adapterName" type="String">The name of the adapter to be added. This matches the name used
|
||||
/// in the data-val-nnnn HTML attribute (where nnnn is the adapter name).</param>
|
||||
/// <param name="params" type="Array" optional="true">[Optional] An array of parameter names (strings) that will
|
||||
/// be extracted from the data-val-nnnn-mmmm HTML attributes (where nnnn is the adapter name, and
|
||||
/// mmmm is the parameter name).</param>
|
||||
/// <param name="fn" type="Function">The function to call, which adapts the values from the HTML
|
||||
/// attributes into jQuery Validate rules and/or messages.</param>
|
||||
/// <returns type="jQuery.validator.unobtrusive.adapters" />
|
||||
if (!fn) { // Called with no params, just a function
|
||||
fn = params;
|
||||
params = [];
|
||||
}
|
||||
this.push({ name: adapterName, params: params, adapt: fn });
|
||||
return this;
|
||||
};
|
||||
|
||||
adapters.addBool = function (adapterName, ruleName) {
|
||||
/// <summary>Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where
|
||||
/// the jQuery Validate validation rule has no parameter values.</summary>
|
||||
/// <param name="adapterName" type="String">The name of the adapter to be added. This matches the name used
|
||||
/// in the data-val-nnnn HTML attribute (where nnnn is the adapter name).</param>
|
||||
/// <param name="ruleName" type="String" optional="true">[Optional] The name of the jQuery Validate rule. If not provided, the value
|
||||
/// of adapterName will be used instead.</param>
|
||||
/// <returns type="jQuery.validator.unobtrusive.adapters" />
|
||||
return this.add(adapterName, function (options) {
|
||||
setValidationValues(options, ruleName || adapterName, true);
|
||||
});
|
||||
};
|
||||
|
||||
adapters.addMinMax = function (adapterName, minRuleName, maxRuleName, minMaxRuleName, minAttribute, maxAttribute) {
|
||||
/// <summary>Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where
|
||||
/// the jQuery Validate validation has three potential rules (one for min-only, one for max-only, and
|
||||
/// one for min-and-max). The HTML parameters are expected to be named -min and -max.</summary>
|
||||
/// <param name="adapterName" type="String">The name of the adapter to be added. This matches the name used
|
||||
/// in the data-val-nnnn HTML attribute (where nnnn is the adapter name).</param>
|
||||
/// <param name="minRuleName" type="String">The name of the jQuery Validate rule to be used when you only
|
||||
/// have a minimum value.</param>
|
||||
/// <param name="maxRuleName" type="String">The name of the jQuery Validate rule to be used when you only
|
||||
/// have a maximum value.</param>
|
||||
/// <param name="minMaxRuleName" type="String">The name of the jQuery Validate rule to be used when you
|
||||
/// have both a minimum and maximum value.</param>
|
||||
/// <param name="minAttribute" type="String" optional="true">[Optional] The name of the HTML attribute that
|
||||
/// contains the minimum value. The default is "min".</param>
|
||||
/// <param name="maxAttribute" type="String" optional="true">[Optional] The name of the HTML attribute that
|
||||
/// contains the maximum value. The default is "max".</param>
|
||||
/// <returns type="jQuery.validator.unobtrusive.adapters" />
|
||||
return this.add(adapterName, [minAttribute || "min", maxAttribute || "max"], function (options) {
|
||||
var min = options.params.min,
|
||||
max = options.params.max;
|
||||
|
||||
if (min && max) {
|
||||
setValidationValues(options, minMaxRuleName, [min, max]);
|
||||
}
|
||||
else if (min) {
|
||||
setValidationValues(options, minRuleName, min);
|
||||
}
|
||||
else if (max) {
|
||||
setValidationValues(options, maxRuleName, max);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
adapters.addSingleVal = function (adapterName, attribute, ruleName) {
|
||||
/// <summary>Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where
|
||||
/// the jQuery Validate validation rule has a single value.</summary>
|
||||
/// <param name="adapterName" type="String">The name of the adapter to be added. This matches the name used
|
||||
/// in the data-val-nnnn HTML attribute(where nnnn is the adapter name).</param>
|
||||
/// <param name="attribute" type="String">[Optional] The name of the HTML attribute that contains the value.
|
||||
/// The default is "val".</param>
|
||||
/// <param name="ruleName" type="String" optional="true">[Optional] The name of the jQuery Validate rule. If not provided, the value
|
||||
/// of adapterName will be used instead.</param>
|
||||
/// <returns type="jQuery.validator.unobtrusive.adapters" />
|
||||
return this.add(adapterName, [attribute || "val"], function (options) {
|
||||
setValidationValues(options, ruleName || adapterName, options.params[attribute]);
|
||||
});
|
||||
};
|
||||
|
||||
$jQval.addMethod("__dummy__", function (value, element, params) {
|
||||
return true;
|
||||
});
|
||||
|
||||
$jQval.addMethod("regex", function (value, element, params) {
|
||||
var match;
|
||||
if (this.optional(element)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
match = new RegExp(params).exec(value);
|
||||
return (match && (match.index === 0) && (match[0].length === value.length));
|
||||
});
|
||||
|
||||
$jQval.addMethod("nonalphamin", function (value, element, nonalphamin) {
|
||||
var match;
|
||||
if (nonalphamin) {
|
||||
match = value.match(/\W/g);
|
||||
match = match && match.length >= nonalphamin;
|
||||
}
|
||||
return match;
|
||||
});
|
||||
|
||||
if ($jQval.methods.extension) {
|
||||
adapters.addSingleVal("accept", "mimtype");
|
||||
adapters.addSingleVal("extension", "extension");
|
||||
} else {
|
||||
// for backward compatibility, when the 'extension' validation method does not exist, such as with versions
|
||||
// of JQuery Validation plugin prior to 1.10, we should use the 'accept' method for
|
||||
// validating the extension, and ignore mime-type validations as they are not supported.
|
||||
adapters.addSingleVal("extension", "extension", "accept");
|
||||
}
|
||||
|
||||
adapters.addSingleVal("regex", "pattern");
|
||||
adapters.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url");
|
||||
adapters.addMinMax("length", "minlength", "maxlength", "rangelength").addMinMax("range", "min", "max", "range");
|
||||
adapters.addMinMax("minlength", "minlength").addMinMax("maxlength", "minlength", "maxlength");
|
||||
adapters.add("equalto", ["other"], function (options) {
|
||||
var prefix = getModelPrefix(options.element.name),
|
||||
other = options.params.other,
|
||||
fullOtherName = appendModelPrefix(other, prefix),
|
||||
element = $(options.form).find(":input").filter("[name='" + escapeAttributeValue(fullOtherName) + "']")[0];
|
||||
|
||||
setValidationValues(options, "equalTo", element);
|
||||
});
|
||||
adapters.add("required", function (options) {
|
||||
// jQuery Validate equates "required" with "mandatory" for checkbox elements
|
||||
if (options.element.tagName.toUpperCase() !== "INPUT" || options.element.type.toUpperCase() !== "CHECKBOX") {
|
||||
setValidationValues(options, "required", true);
|
||||
}
|
||||
});
|
||||
adapters.add("remote", ["url", "type", "additionalfields"], function (options) {
|
||||
var value = {
|
||||
url: options.params.url,
|
||||
type: options.params.type || "GET",
|
||||
data: {}
|
||||
},
|
||||
prefix = getModelPrefix(options.element.name);
|
||||
|
||||
$.each(splitAndTrim(options.params.additionalfields || options.element.name), function (i, fieldName) {
|
||||
var paramName = appendModelPrefix(fieldName, prefix);
|
||||
value.data[paramName] = function () {
|
||||
var field = $(options.form).find(":input").filter("[name='" + escapeAttributeValue(paramName) + "']");
|
||||
// For checkboxes and radio buttons, only pick up values from checked fields.
|
||||
if (field.is(":checkbox")) {
|
||||
return field.filter(":checked").val() || field.filter(":hidden").val() || '';
|
||||
}
|
||||
else if (field.is(":radio")) {
|
||||
return field.filter(":checked").val() || '';
|
||||
}
|
||||
return field.val();
|
||||
};
|
||||
});
|
||||
|
||||
setValidationValues(options, "remote", value);
|
||||
});
|
||||
adapters.add("password", ["min", "nonalphamin", "regex"], function (options) {
|
||||
if (options.params.min) {
|
||||
setValidationValues(options, "minlength", options.params.min);
|
||||
}
|
||||
if (options.params.nonalphamin) {
|
||||
setValidationValues(options, "nonalphamin", options.params.nonalphamin);
|
||||
}
|
||||
if (options.params.regex) {
|
||||
setValidationValues(options, "regex", options.params.regex);
|
||||
}
|
||||
});
|
||||
adapters.add("fileextensions", ["extensions"], function (options) {
|
||||
setValidationValues(options, "extension", options.params.extensions);
|
||||
});
|
||||
|
||||
$(function () {
|
||||
$jQval.unobtrusive.parse(document);
|
||||
});
|
||||
|
||||
return $jQval.unobtrusive;
|
||||
}));
|
||||
@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
=====================
|
||||
|
||||
Copyright Jörn Zaefferer
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||