init
This commit is contained in:
commit
2b51c24cdf
124
.dockerignore
Normal file
124
.dockerignore
Normal file
@ -0,0 +1,124 @@
|
||||
# Build Folders (you can keep bin if you'd like, to store dlls and pdbs)
|
||||
**/[Bb]in/
|
||||
**/[Oo]bj/
|
||||
node_modules/
|
||||
dist/
|
||||
|
||||
# mstest test results
|
||||
TestResults
|
||||
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.sln.docstates
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Rr]elease/
|
||||
x64/
|
||||
*_i.c
|
||||
*_p.c
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*
|
||||
|
||||
# NCrunch
|
||||
*.ncrunch*
|
||||
.*crunch*.local.xml
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish
|
||||
|
||||
# Publish Web Output
|
||||
*.Publish.xml
|
||||
|
||||
# NuGet Packages Directory
|
||||
packages
|
||||
|
||||
# Windows Azure Build Output
|
||||
csx
|
||||
*.build.csdef
|
||||
|
||||
# Windows Store app package directory
|
||||
AppPackages/
|
||||
|
||||
# Others
|
||||
[Bb]in
|
||||
[Oo]bj
|
||||
sql
|
||||
TestResults
|
||||
[Tt]est[Rr]esult*
|
||||
*.Cache
|
||||
ClientBin
|
||||
[Ss]tyle[Cc]op.*
|
||||
~$*
|
||||
*.dbmdl
|
||||
Generated_Code #added for RIA/Silverlight projects
|
||||
|
||||
# Backup & report files from converting an old project file to a newer
|
||||
# Visual Studio version. Backup files are not needed, because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
|
||||
src/Rapporteringsregisteret.Web/assets/less/*.css
|
||||
|
||||
MetricResults/
|
||||
*.sln.ide/
|
||||
|
||||
_configs/
|
||||
|
||||
# vnext stuff
|
||||
bower_components
|
||||
output
|
||||
|
||||
.vs
|
||||
**/launchSettings.json
|
||||
153
.editorconfig
Normal file
153
.editorconfig
Normal file
@ -0,0 +1,153 @@
|
||||
# editorconfig.org
|
||||
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# Default settings:
|
||||
# A newline ending every file
|
||||
# Use 4 spaces as indentation
|
||||
[*]
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
charset = utf-8
|
||||
|
||||
[launchSettings.json]
|
||||
indent_size = 2
|
||||
|
||||
# C# files
|
||||
[*.cs]
|
||||
# New line preferences
|
||||
csharp_new_line_before_open_brace = all
|
||||
csharp_new_line_before_else = true
|
||||
csharp_new_line_before_catch = true
|
||||
csharp_new_line_before_finally = true
|
||||
csharp_new_line_before_members_in_object_initializers = true
|
||||
csharp_new_line_before_members_in_anonymous_types = true
|
||||
csharp_new_line_within_query_expression_clauses = true
|
||||
|
||||
# Indentation preferences
|
||||
csharp_indent_block_contents = true
|
||||
csharp_indent_braces = false
|
||||
csharp_indent_case_contents = true
|
||||
csharp_indent_switch_labels = true
|
||||
csharp_indent_labels = flush_left
|
||||
|
||||
# avoid this. unless absolutely necessary
|
||||
dotnet_style_qualification_for_field = false:suggestion
|
||||
dotnet_style_qualification_for_property = false:suggestion
|
||||
dotnet_style_qualification_for_method = false:suggestion
|
||||
dotnet_style_qualification_for_event = false:suggestion
|
||||
|
||||
# only use var when it's obvious what the variable type is
|
||||
csharp_style_var_for_built_in_types = false:none
|
||||
csharp_style_var_when_type_is_apparent = false:none
|
||||
csharp_style_var_elsewhere = false:suggestion
|
||||
|
||||
# use language keywords instead of BCL types
|
||||
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
|
||||
dotnet_style_predefined_type_for_member_access = true:suggestion
|
||||
|
||||
# name all constant fields using PascalCase
|
||||
dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
|
||||
dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
|
||||
dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
|
||||
|
||||
dotnet_naming_symbols.constant_fields.applicable_kinds = field
|
||||
dotnet_naming_symbols.constant_fields.required_modifiers = const
|
||||
|
||||
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
|
||||
|
||||
# internal and private fields should be _camelCase
|
||||
dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion
|
||||
dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields
|
||||
dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style
|
||||
|
||||
dotnet_naming_symbols.private_internal_fields.applicable_kinds = field
|
||||
dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal
|
||||
|
||||
dotnet_naming_style.camel_case_underscore_style.required_prefix = _
|
||||
dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case
|
||||
|
||||
# Code style defaults
|
||||
dotnet_sort_system_directives_first = true
|
||||
csharp_preserve_single_line_blocks = true
|
||||
csharp_preserve_single_line_statements = false
|
||||
|
||||
# Expression-level preferences
|
||||
dotnet_style_object_initializer = true:suggestion
|
||||
dotnet_style_collection_initializer = true:suggestion
|
||||
dotnet_style_explicit_tuple_names = true:suggestion
|
||||
dotnet_style_coalesce_expression = true:suggestion
|
||||
dotnet_style_null_propagation = true:suggestion
|
||||
|
||||
# Expression-bodied members
|
||||
csharp_style_expression_bodied_methods = false:none
|
||||
csharp_style_expression_bodied_constructors = false:none
|
||||
csharp_style_expression_bodied_operators = false:none
|
||||
csharp_style_expression_bodied_properties = true:none
|
||||
csharp_style_expression_bodied_indexers = true:none
|
||||
csharp_style_expression_bodied_accessors = true:none
|
||||
|
||||
# Pattern matching
|
||||
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
|
||||
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
|
||||
csharp_style_inlined_variable_declaration = true:suggestion
|
||||
|
||||
# Null checking preferences
|
||||
csharp_style_throw_expression = true:suggestion
|
||||
csharp_style_conditional_delegate_call = true:suggestion
|
||||
|
||||
# Space preferences
|
||||
csharp_space_after_cast = false
|
||||
csharp_space_after_colon_in_inheritance_clause = true
|
||||
csharp_space_after_comma = true
|
||||
csharp_space_after_dot = false
|
||||
csharp_space_after_keywords_in_control_flow_statements = true
|
||||
csharp_space_after_semicolon_in_for_statement = true
|
||||
csharp_space_around_binary_operators = before_and_after
|
||||
csharp_space_around_declaration_statements = do_not_ignore
|
||||
csharp_space_before_colon_in_inheritance_clause = true
|
||||
csharp_space_before_comma = false
|
||||
csharp_space_before_dot = false
|
||||
csharp_space_before_open_square_brackets = false
|
||||
csharp_space_before_semicolon_in_for_statement = false
|
||||
csharp_space_between_empty_square_brackets = false
|
||||
csharp_space_between_method_call_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_call_name_and_opening_parenthesis = false
|
||||
csharp_space_between_method_call_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_name_and_open_parenthesis = false
|
||||
csharp_space_between_method_declaration_parameter_list_parentheses = false
|
||||
csharp_space_between_parentheses = false
|
||||
csharp_space_between_square_brackets = false
|
||||
csharp_style_prefer_null_check_over_type_check = true:warning
|
||||
csharp_prefer_simple_using_statement = true:warning
|
||||
|
||||
# C++ Files
|
||||
|
||||
[*.{cpp,h,in}]
|
||||
curly_bracket_next_line = true
|
||||
indent_brace_style = Allman
|
||||
|
||||
# Xml project files
|
||||
[*.{csproj,vcxproj,vcxproj.filters,proj,nativeproj,locproj}]
|
||||
indent_size = 2
|
||||
|
||||
# Xml build files
|
||||
[*.builds]
|
||||
indent_size = 2
|
||||
|
||||
# Xml files
|
||||
[*.{xml,stylecop,resx,ruleset}]
|
||||
indent_size = 2
|
||||
|
||||
# Xml config files
|
||||
[*.{props,targets,config,nuspec}]
|
||||
indent_size = 2
|
||||
|
||||
# Shell scripts
|
||||
[*.sh]
|
||||
end_of_line = lf
|
||||
[*.{cmd, bat}]
|
||||
end_of_line = crlf
|
||||
17
.gitattributes
vendored
Normal file
17
.gitattributes
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
# Set the default behavior, in case people don't have core.autocrlf set.
|
||||
* text=auto
|
||||
|
||||
# Explicitly declare text files you want to always be normalized and converted
|
||||
# to native line endings on checkout.
|
||||
*.c text
|
||||
*.h text
|
||||
|
||||
# Declare files that will always have CRLF line endings on checkout.
|
||||
*.sln text eol=crlf
|
||||
|
||||
# Declare files that will always have CRLF line endings on checkout.
|
||||
*.sh text eol=lf
|
||||
|
||||
# Denote all files that are truly binary and should not be modified.
|
||||
*.png binary
|
||||
*.jpg binary
|
||||
304
.gitignore
vendored
Normal file
304
.gitignore
vendored
Normal file
@ -0,0 +1,304 @@
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
.vs/
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
|
||||
# Visual Studio 2015 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUNIT
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_i.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# JustCode is a .NET coding add-in
|
||||
.JustCode
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# TODO: Comment the next line if you want to checkin your web deploy settings
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/packages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/packages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/packages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Typescript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# JetBrains Rider
|
||||
.idea/
|
||||
*.sln.iml
|
||||
|
||||
# CodeRush
|
||||
.cr/
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
/BTCPayServer/Build/dockerfiles
|
||||
|
||||
# Bundling JS/CSS
|
||||
BTCPayServer/wwwroot/bundles/*
|
||||
!BTCPayServer/wwwroot/bundles/.gitignore
|
||||
|
||||
.vscode/*
|
||||
!.vscode/launch.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/extensions.json
|
||||
BTCPayServer/testpwd
|
||||
.DS_Store
|
||||
Packed Plugins
|
||||
Plugins/packed
|
||||
|
||||
BTCPayServer/wwwroot/swagger/v1/openapi.json
|
||||
19
PluginBuilder.Targets/Class1.cs
Normal file
19
PluginBuilder.Targets/Class1.cs
Normal file
@ -0,0 +1,19 @@
|
||||
#nullable disable
|
||||
using System.Reflection;
|
||||
using Microsoft.Build.Framework;
|
||||
|
||||
namespace PluginBuilder.Targets;
|
||||
public class Packer : ITask
|
||||
{
|
||||
public IBuildEngine BuildEngine { get; set; }
|
||||
public ITaskHost HostObject { get; set; }
|
||||
|
||||
public bool Execute()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public string PluginDll { get; set; }
|
||||
[Output]
|
||||
public string Output { get; set; }
|
||||
}
|
||||
13
PluginBuilder.Targets/PluginBuilder.Targets.csproj
Normal file
13
PluginBuilder.Targets/PluginBuilder.Targets.csproj
Normal file
@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Build.Framework" Version="17.3.2" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
27
PluginBuilder.Tests/PluginBuilder.Tests.csproj
Normal file
27
PluginBuilder.Tests/PluginBuilder.Tests.csproj
Normal file
@ -0,0 +1,27 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.0">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\PluginBuilder\PluginBuilder.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
70
PluginBuilder.Tests/ServerTester.cs
Normal file
70
PluginBuilder.Tests/ServerTester.cs
Normal file
@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace PluginBuilder.Tests
|
||||
{
|
||||
public class ServerTester : IAsyncDisposable
|
||||
{
|
||||
public ServerTester(string testFolder, XUnitLogger logs)
|
||||
{
|
||||
TestFolder = testFolder;
|
||||
Logs = logs;
|
||||
}
|
||||
|
||||
public string TestFolder { get; }
|
||||
|
||||
public T GetService<T>() where T : notnull
|
||||
{
|
||||
return WebApp.Services.GetRequiredService<T>();
|
||||
}
|
||||
|
||||
public XUnitLogger Logs { get; }
|
||||
|
||||
List<IAsyncDisposable> disposables = new List<IAsyncDisposable>();
|
||||
WebApplication? _WebApp;
|
||||
public WebApplication WebApp
|
||||
{
|
||||
get
|
||||
{
|
||||
return _WebApp ?? throw new InvalidOperationException("Webapp not initialized");
|
||||
}
|
||||
}
|
||||
public bool ReuseDatabase { get; set; } = true;
|
||||
public async Task Start()
|
||||
{
|
||||
string dbName = TestFolder;
|
||||
if (!ReuseDatabase)
|
||||
{
|
||||
dbName = TestFolder + "_" + (DateTimeOffset.UtcNow.Ticks / 100000).ToString();
|
||||
}
|
||||
dbName = dbName.ToLowerInvariant();
|
||||
Logs.LogInformation($"DbName: {dbName}");
|
||||
Environment.SetEnvironmentVariable("PB_POSTGRES", "User ID=postgres;Include Error Detail=true;Host=127.0.0.1;Port=61932;Database=" + dbName);
|
||||
Environment.SetEnvironmentVariable("PB_STORAGE_CONNECTION_STRING", "BlobEndpoint=http://127.0.0.1:32827/satoshi;AccountName=satoshi;AccountKey=Rxb41pUHRe+ibX5XS311tjXpjvu7mVi2xYJvtmq1j2jlUpN+fY/gkzyBMjqwzgj42geXGdYSbPEcu5i5wjSjPw==");
|
||||
var host = new PluginBuilder.Program();
|
||||
var webappBuilder = host.CreateWebApplicationBuilder();
|
||||
webappBuilder.Logging.AddFilter(typeof(ProcessRunner).FullName, LogLevel.Trace);
|
||||
webappBuilder.Logging.AddProvider(Logs);
|
||||
var webapp = webappBuilder.Build();
|
||||
host.Configure(webapp);
|
||||
disposables.Add(webapp);
|
||||
await webapp.StartAsync();
|
||||
_WebApp = webapp;
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
foreach (var d in disposables)
|
||||
{
|
||||
await d.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
53
PluginBuilder.Tests/UnitTest1.cs
Normal file
53
PluginBuilder.Tests/UnitTest1.cs
Normal file
@ -0,0 +1,53 @@
|
||||
using System.Threading.Tasks;
|
||||
using PluginBuilder.Services;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace PluginBuilder.Tests;
|
||||
|
||||
public class UnitTest1 : UnitTestBase
|
||||
{
|
||||
public UnitTest1(ITestOutputHelper logs) : base(logs)
|
||||
{
|
||||
|
||||
}
|
||||
[Fact]
|
||||
public async Task Test1()
|
||||
{
|
||||
await using var tester = await Start();
|
||||
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("test-6", true)]
|
||||
[InlineData("test-6-", false)]
|
||||
[InlineData("6test-6", false)]
|
||||
[InlineData("-test-6", false)]
|
||||
[InlineData("te", false)]
|
||||
[InlineData("teqoeteqoeteqoeteqoeteqoeteqoee", false)]
|
||||
[InlineData("teqoeteqoeteqoeteqoeteqoet", true)]
|
||||
public void IsValidSlugTest(string slug, bool expected)
|
||||
{
|
||||
Assert.Equal(expected, PluginSlug.IsValidSlugName(slug));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanPackPlugin()
|
||||
{
|
||||
await using var tester = Create();
|
||||
tester.ReuseDatabase = false;
|
||||
await tester.Start();
|
||||
|
||||
var buildService = tester.GetService<BuildService>();
|
||||
using var conn = await tester.GetService<DBConnectionFactory>().Open();
|
||||
await conn.NewPlugin("rockstar-stylist");
|
||||
|
||||
//https://github.com/Kukks/btcpayserver/tree/plugins/collection/Plugins/BTCPayServer.Plugins.RockstarStylist
|
||||
await buildService.Build("rockstar-stylist",
|
||||
new PluginBuildParameters("https://github.com/Kukks/btcpayserver")
|
||||
{
|
||||
PluginDirectory = "Plugins/BTCPayServer.Plugins.RockstarStylist",
|
||||
GitRef = "plugins/collection"
|
||||
});
|
||||
}
|
||||
}
|
||||
33
PluginBuilder.Tests/UnitTestBase.cs
Normal file
33
PluginBuilder.Tests/UnitTestBase.cs
Normal file
@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace PluginBuilder.Tests
|
||||
{
|
||||
public class UnitTestBase
|
||||
{
|
||||
public UnitTestBase(ITestOutputHelper log)
|
||||
{
|
||||
Log = new XUnitLogger("Tests", log);
|
||||
}
|
||||
|
||||
public XUnitLogger Log { get; }
|
||||
|
||||
|
||||
public async Task<ServerTester> Start([CallerMemberName] string? caller = null)
|
||||
{
|
||||
ServerTester tester = Create(caller);
|
||||
await tester.Start();
|
||||
return tester;
|
||||
}
|
||||
|
||||
public ServerTester Create([CallerMemberName] string? caller = null)
|
||||
{
|
||||
return new ServerTester(caller ?? "Default", Log);
|
||||
}
|
||||
}
|
||||
}
|
||||
82
PluginBuilder.Tests/XUnitLogger.cs
Normal file
82
PluginBuilder.Tests/XUnitLogger.cs
Normal file
@ -0,0 +1,82 @@
|
||||
#nullable disable
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace PluginBuilder.Tests
|
||||
{
|
||||
public class XUnitLogger : ILogger, ILoggerProvider, ILoggerFactory
|
||||
{
|
||||
public XUnitLogger(ITestOutputHelper log)
|
||||
{
|
||||
this.log = log;
|
||||
}
|
||||
string category;
|
||||
public ITestOutputHelper log;
|
||||
|
||||
public XUnitLogger(string category, ITestOutputHelper log)
|
||||
{
|
||||
this.category = category;
|
||||
this.log = log;
|
||||
}
|
||||
public IDisposable BeginScope<TState>(TState state)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public ILogger CreateLogger(string categoryName)
|
||||
{
|
||||
return new XUnitLogger(categoryName, log);
|
||||
}
|
||||
public ILogger<T> CreateLogger<T>()
|
||||
{
|
||||
return new Logger<T>(this);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public bool IsEnabled(LogLevel logLevel)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
|
||||
{
|
||||
log.WriteLine($"[{Simplified(category)}] {Simplified(logLevel)}: {formatter(state, exception)}");
|
||||
if (exception is Exception)
|
||||
{
|
||||
log.WriteLine($"Exception: {exception}");
|
||||
}
|
||||
}
|
||||
|
||||
private string Simplified(LogLevel logLevel)
|
||||
{
|
||||
switch (logLevel)
|
||||
{
|
||||
case LogLevel.Information:
|
||||
return "Info";
|
||||
case LogLevel.Warning:
|
||||
return "Warn";
|
||||
default:
|
||||
return logLevel.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
private string Simplified(string category)
|
||||
{
|
||||
return category.Split('.').Last();
|
||||
//return category;
|
||||
}
|
||||
|
||||
public void AddProvider(ILoggerProvider provider)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
18
PluginBuilder.Tests/docker-compose.yml
Normal file
18
PluginBuilder.Tests/docker-compose.yml
Normal file
@ -0,0 +1,18 @@
|
||||
version: "3"
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15.0
|
||||
environment:
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
ports:
|
||||
- "61932:5432"
|
||||
expose:
|
||||
- "5432"
|
||||
|
||||
azureblob:
|
||||
image: mcr.microsoft.com/azure-blob-storage
|
||||
environment:
|
||||
LOCAL_STORAGE_ACCOUNT_NAME: satoshi
|
||||
LOCAL_STORAGE_ACCOUNT_KEY: Rxb41pUHRe+ibX5XS311tjXpjvu7mVi2xYJvtmq1j2jlUpN+fY/gkzyBMjqwzgj42geXGdYSbPEcu5i5wjSjPw==
|
||||
ports:
|
||||
- "32827:11002"
|
||||
26
PluginBuilder/ConfigurationExtensions.cs
Normal file
26
PluginBuilder/ConfigurationExtensions.cs
Normal file
@ -0,0 +1,26 @@
|
||||
namespace PluginBuilder
|
||||
{
|
||||
public class ConfigurationRequiredException : ConfigurationException
|
||||
{
|
||||
public ConfigurationRequiredException(string key) : base(key, $"Required environment variable")
|
||||
{
|
||||
}
|
||||
}
|
||||
public class ConfigurationException : Exception
|
||||
{
|
||||
public ConfigurationException(string key, string message) : base ($"[{key}] {message}")
|
||||
{
|
||||
Key = key;
|
||||
}
|
||||
public string Key { get; }
|
||||
}
|
||||
public static class ConfigurationExtensions
|
||||
{
|
||||
public static string GetRequired(this IConfiguration configuration, string key)
|
||||
{
|
||||
if (configuration[key] is string v && !string.IsNullOrWhiteSpace(v))
|
||||
return v;
|
||||
throw new ConfigurationRequiredException(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
33
PluginBuilder/Controllers/HomeController.cs
Normal file
33
PluginBuilder/Controllers/HomeController.cs
Normal file
@ -0,0 +1,33 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using PluginBuilder.Services;
|
||||
|
||||
namespace PluginBuilder.Controllers
|
||||
{
|
||||
public class HomeController : Controller
|
||||
{
|
||||
public DBConnectionFactory ConnectionFactory { get; }
|
||||
public HomeController(DBConnectionFactory connectionFactory)
|
||||
{
|
||||
ConnectionFactory = connectionFactory;
|
||||
}
|
||||
[HttpGet("/")]
|
||||
public IActionResult HomePage()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
[HttpPost("/plugins/add")]
|
||||
public IActionResult AddPlugin(
|
||||
string name,
|
||||
string repository,
|
||||
string reference,
|
||||
string csprojPath)
|
||||
{
|
||||
|
||||
// Wouter style: https://github.com/storefront-bvba/btcpayserver-kraken-plugin
|
||||
// Dennis style: https://github.com/dennisreimann/btcpayserver
|
||||
// Kukks sytle: https://github.com/Kukks/btcpayserver/tree/plugins/collection/Plugins
|
||||
return View();
|
||||
}
|
||||
}
|
||||
}
|
||||
3
PluginBuilder/Data/Scripts/01.migrations.sql
Normal file
3
PluginBuilder/Data/Scripts/01.migrations.sql
Normal file
@ -0,0 +1,3 @@
|
||||
CREATE TABLE migrations (
|
||||
script_name TEXT NOT NULL PRIMARY KEY,
|
||||
executed_at timestamptz DEFAULT CURRENT_TIMESTAMP);
|
||||
35
PluginBuilder/Data/Scripts/02.Init.sql
Normal file
35
PluginBuilder/Data/Scripts/02.Init.sql
Normal file
@ -0,0 +1,35 @@
|
||||
CREATE TABLE plugins
|
||||
(
|
||||
slug TEXT NOT NULL PRIMARY KEY
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE builds_ids
|
||||
(
|
||||
plugin_slug TEXT NOT NULL,
|
||||
curr_id BIGINT NOT NULL,
|
||||
PRIMARY KEY (plugin_slug),
|
||||
FOREIGN KEY (plugin_slug) REFERENCES plugins (slug) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE builds
|
||||
(
|
||||
plugin_slug TEXT NOT NULL,
|
||||
id BIGINT NOT NULL,
|
||||
state TEXT NOT NULL,
|
||||
manifest_info JSONB,
|
||||
build_info JSONB,
|
||||
PRIMARY KEY (plugin_slug, id),
|
||||
FOREIGN KEY (plugin_slug) REFERENCES plugins (slug) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE versions
|
||||
(
|
||||
plugin_slug TEXT NOT NULL,
|
||||
ver TEXT NOT NULL,
|
||||
build_id BIGINT NOT NULL,
|
||||
btcpay_min_ver INT[] NOT NULL,
|
||||
PRIMARY KEY (plugin_slug, ver),
|
||||
FOREIGN KEY (plugin_slug) REFERENCES plugins (slug) ON DELETE CASCADE
|
||||
FOREIGN KEY (build_id) REFERENCES builds (id) ON DELETE CASCADE
|
||||
);
|
||||
22
PluginBuilder/FullBuildId.cs
Normal file
22
PluginBuilder/FullBuildId.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace PluginBuilder
|
||||
{
|
||||
public record FullBuildId
|
||||
{
|
||||
public FullBuildId(PluginSlug PluginSlug, long BuildId)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(PluginSlug);
|
||||
if (BuildId < 0)
|
||||
throw new ArgumentException("BuildId should be more than 0", nameof(BuildId));
|
||||
this.PluginSlug = PluginSlug;
|
||||
this.BuildId = BuildId;
|
||||
}
|
||||
public PluginSlug PluginSlug { get; }
|
||||
public long BuildId { get; }
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{PluginSlug}/{BuildId}";
|
||||
}
|
||||
}
|
||||
}
|
||||
24
PluginBuilder/HostedServices/AzureStartupHostedService.cs
Normal file
24
PluginBuilder/HostedServices/AzureStartupHostedService.cs
Normal file
@ -0,0 +1,24 @@
|
||||
using PluginBuilder.Services;
|
||||
|
||||
namespace PluginBuilder.HostedServices
|
||||
{
|
||||
public class AzureStartupHostedService : IHostedService
|
||||
{
|
||||
public AzureStartupHostedService(AzureStorageClient azureStorageClient)
|
||||
{
|
||||
AzureStorageClient = azureStorageClient;
|
||||
}
|
||||
|
||||
public AzureStorageClient AzureStorageClient { get; }
|
||||
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
await AzureStorageClient.EnsureDefaultContainerExists(cancellationToken);
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
84
PluginBuilder/HostedServices/DatabaseStartupHostedService.cs
Normal file
84
PluginBuilder/HostedServices/DatabaseStartupHostedService.cs
Normal file
@ -0,0 +1,84 @@
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using Dapper;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using PluginBuilder.Services;
|
||||
|
||||
namespace PluginBuilder.HostedServices
|
||||
{
|
||||
public class DatabaseStartupHostedService : IHostedService
|
||||
{
|
||||
ILogger Logger { get; }
|
||||
public DatabaseStartupHostedService(ILogger<DatabaseStartupHostedService> logger, DBConnectionFactory connectionFactory)
|
||||
{
|
||||
ConnectionFactory = connectionFactory;
|
||||
Logger = logger;
|
||||
}
|
||||
|
||||
public DBConnectionFactory ConnectionFactory { get; }
|
||||
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
retry:
|
||||
try
|
||||
{
|
||||
await using var conn = await ConnectionFactory.Open();
|
||||
await RunScripts(conn);
|
||||
}
|
||||
catch (Npgsql.NpgsqlException pgex) when (pgex.SqlState == "3D000")
|
||||
{
|
||||
var builder = new Npgsql.NpgsqlConnectionStringBuilder(ConnectionFactory.ConnectionString.ToString());
|
||||
var dbname = builder.Database;
|
||||
Logger.LogInformation($"Database '{dbname}' doesn't exists, creating it...");
|
||||
builder.Database = null;
|
||||
var conn2Str = builder.ToString();
|
||||
var conn2 = new Npgsql.NpgsqlConnection(conn2Str);
|
||||
await conn2.OpenAsync();
|
||||
await conn2.ExecuteAsync($"CREATE DATABASE {dbname} TEMPLATE 'template0' LC_CTYPE 'C' LC_COLLATE 'C' ENCODING 'UTF8'");
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RunScripts(Npgsql.NpgsqlConnection conn)
|
||||
{
|
||||
await using (conn)
|
||||
{
|
||||
HashSet<string> executed;
|
||||
try
|
||||
{
|
||||
executed = (await conn.QueryAsync<string>("SELECT script_name FROM migrations")).ToHashSet();
|
||||
}
|
||||
catch (Npgsql.NpgsqlException ex) when (ex.SqlState == "42P01")
|
||||
{
|
||||
executed = new HashSet<string>();
|
||||
}
|
||||
foreach (var resource in System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceNames()
|
||||
.Where(n => n.EndsWith(".sql", System.StringComparison.InvariantCulture))
|
||||
.OrderBy(n => n))
|
||||
{
|
||||
var parts = resource.Split('.');
|
||||
if (!int.TryParse(parts[^3], NumberStyles.Any, CultureInfo.InvariantCulture, out _))
|
||||
continue;
|
||||
var scriptName = $"{parts[^3]}.{parts[^2]}";
|
||||
if (executed.Contains(scriptName))
|
||||
continue;
|
||||
var stream = System.Reflection.Assembly.GetExecutingAssembly()
|
||||
.GetManifestResourceStream(resource)!;
|
||||
string content;
|
||||
using (var reader = new StreamReader(stream, Encoding.UTF8))
|
||||
{
|
||||
content = reader.ReadToEnd();
|
||||
}
|
||||
Logger.LogInformation($"Execute script {scriptName}...");
|
||||
await conn.ExecuteAsync($"{content}; INSERT INTO migrations VALUES (@scriptName)", new { scriptName });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
76
PluginBuilder/HostedServices/DockerStartupHostedService.cs
Normal file
76
PluginBuilder/HostedServices/DockerStartupHostedService.cs
Normal file
@ -0,0 +1,76 @@
|
||||
namespace PluginBuilder.HostedServices
|
||||
{
|
||||
public class DockerStartupException : Exception
|
||||
{
|
||||
public DockerStartupException(string message) : base(message)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
public class DockerStartupHostedService : IHostedService
|
||||
{
|
||||
public DockerStartupHostedService(ILogger<DockerStartupHostedService> logger, IWebHostEnvironment env, ProcessRunner processRunner)
|
||||
{
|
||||
Logger = logger;
|
||||
ProcessRunner = processRunner;
|
||||
ContentRootPath = env.ContentRootPath;
|
||||
}
|
||||
|
||||
public ILogger<DockerStartupHostedService> Logger { get; }
|
||||
public ProcessRunner ProcessRunner { get; }
|
||||
public string ContentRootPath { get; }
|
||||
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
Logger.LogInformation("Building the PluginBuilder docker image");
|
||||
var result = await ProcessRunner.RunAsync(new ProcessSpec()
|
||||
{
|
||||
Executable = "docker",
|
||||
Arguments = new[]
|
||||
{
|
||||
"build",
|
||||
"-f", "PluginBuilder.Dockerfile",
|
||||
"-t", "plugin-builder",
|
||||
"."
|
||||
},
|
||||
WorkingDirectory = ContentRootPath
|
||||
}, cancellationToken);
|
||||
if (result != 0)
|
||||
throw new DockerStartupException("The build of PluginBuilder.Dockerfile failed");
|
||||
var output = new OutputCapture();
|
||||
await ProcessRunner.RunAsync(new ProcessSpec()
|
||||
{
|
||||
Executable = "docker",
|
||||
Arguments = new[]
|
||||
{
|
||||
"volume", "ls",
|
||||
"-f", "label=BTCPAY_PLUGIN_BUILD",
|
||||
"--format", "{{ .Name }}"
|
||||
},
|
||||
OutputCapture = output
|
||||
}, cancellationToken);
|
||||
if (result != 0)
|
||||
throw new DockerStartupException("docker volume ls failed");
|
||||
if (output.Lines.Any())
|
||||
{
|
||||
Logger.LogInformation("Cleaning dangling volumes");
|
||||
var args = new List<string>();
|
||||
args.Add("volume");
|
||||
args.Add("rm");
|
||||
args.AddRange(output.Lines);
|
||||
await ProcessRunner.RunAsync(new ProcessSpec()
|
||||
{
|
||||
Executable = "docker",
|
||||
Arguments = args.ToArray()
|
||||
}, cancellationToken);
|
||||
if (result != 0)
|
||||
throw new DockerStartupException("docker volume rm failed");
|
||||
}
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
51
PluginBuilder/NpgsqlConnectionExtensions.cs
Normal file
51
PluginBuilder/NpgsqlConnectionExtensions.cs
Normal file
@ -0,0 +1,51 @@
|
||||
using Dapper;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Npgsql;
|
||||
|
||||
namespace PluginBuilder
|
||||
{
|
||||
public static class NpgsqlConnectionExtensions
|
||||
{
|
||||
public static async Task NewPlugin(this NpgsqlConnection connection, PluginSlug pluginSlug)
|
||||
{
|
||||
await connection.ExecuteAsync("INSERT INTO plugins (slug) VALUES (@id);",
|
||||
new
|
||||
{
|
||||
id = pluginSlug.ToString(),
|
||||
});
|
||||
}
|
||||
public static async Task UpdateBuild(this NpgsqlConnection connection, FullBuildId fullBuildId, string newState, JObject? buildInfo, JObject? manifestInfo = null)
|
||||
{
|
||||
await connection.ExecuteAsync(
|
||||
"UPDATE builds " +
|
||||
"SET state=@state, " +
|
||||
"build_info=COALESCE(build_info || @build_info::JSONB, @build_info::JSONB, build_info), " +
|
||||
"manifest_info=COALESCE(@manifest_info::JSONB, manifest_info) " +
|
||||
"WHERE plugin_slug=@plugin_slug AND id=@buildId",
|
||||
new
|
||||
{
|
||||
state = newState,
|
||||
build_info = buildInfo?.ToString(),
|
||||
manifest_info = manifestInfo?.ToString(),
|
||||
plugin_slug = fullBuildId.PluginSlug.ToString(),
|
||||
buildId = fullBuildId.BuildId
|
||||
});
|
||||
}
|
||||
public static Task<long> NewBuild(this NpgsqlConnection connection, PluginSlug pluginSlug)
|
||||
{
|
||||
return connection.ExecuteScalarAsync<long>("" +
|
||||
"WITH cte AS " +
|
||||
"( " +
|
||||
" INSERT INTO builds_ids AS bi VALUES (@plugin_slug, 0)" +
|
||||
" ON CONFLICT (plugin_slug) DO UPDATE SET curr_id=bi.curr_id+1 " +
|
||||
" RETURNING curr_id " +
|
||||
") " +
|
||||
"INSERT INTO builds (plugin_slug, id, state) VALUES (@plugin_slug, (SELECT * FROM cte), @state) RETURNING id;",
|
||||
new
|
||||
{
|
||||
plugin_slug = pluginSlug.ToString(),
|
||||
state = "queued"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
14
PluginBuilder/PluginBuildParameters.cs
Normal file
14
PluginBuilder/PluginBuildParameters.cs
Normal file
@ -0,0 +1,14 @@
|
||||
namespace PluginBuilder
|
||||
{
|
||||
public class PluginBuildParameters
|
||||
{
|
||||
public PluginBuildParameters(string gitRepository)
|
||||
{
|
||||
GitRepository = gitRepository;
|
||||
}
|
||||
public string GitRepository { get; set; }
|
||||
public string? GitRef { get; set; }
|
||||
public string? PluginDirectory { get; set; }
|
||||
public string? BuildConfig { get; set; }
|
||||
}
|
||||
}
|
||||
21
PluginBuilder/PluginBuilder.Dockerfile
Normal file
21
PluginBuilder/PluginBuilder.Dockerfile
Normal file
@ -0,0 +1,21 @@
|
||||
FROM mcr.microsoft.com/dotnet/sdk:6.0
|
||||
|
||||
RUN apt-get update && apt-get install -y git jq && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
||||
RUN useradd -r --create-home dotnet
|
||||
USER dotnet
|
||||
|
||||
WORKDIR /build-tools
|
||||
ENV PLUGIN_PACKER_VERSION=https://github.com/btcpayserver/btcpayserver
|
||||
RUN git clone --depth 1 -b v1.6.12 --single-branch https://github.com/btcpayserver/btcpayserver && \
|
||||
cd btcpayserver/BTCPayServer.PluginPacker && \
|
||||
dotnet build -c Release -o "/build-tools/PluginPacker" && \
|
||||
rm -rf /build-tools/btcpayserver
|
||||
|
||||
|
||||
WORKDIR /out
|
||||
WORKDIR /build
|
||||
COPY --chown=dotnet:dotnet entrypoint.sh /entrypoint.sh
|
||||
|
||||
CMD [ "/entrypoint.sh" ]
|
||||
43
PluginBuilder/PluginBuilder.csproj
Normal file
43
PluginBuilder/PluginBuilder.csproj
Normal file
@ -0,0 +1,43 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UserSecretsId>aspnet-PluginBuilder-4D3592EF-6E6A-41BD-960D-231C299188A2</UserSecretsId>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="wwwroot\" />
|
||||
<Folder Include="wwwroot\img\" />
|
||||
<Folder Include="wwwroot\scripts\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Dapper" Version="2.0.123" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="Npgsql" Version="6.0.7" />
|
||||
<PackageReference Include="WindowsAzure.Storage" Version="9.3.3" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<DisableScopedCssBundling>true</DisableScopedCssBundling>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Data\Scripts\*.sql" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Data\Scripts\01.migrations.sql" />
|
||||
<None Remove="Data\Scripts\02.Init.sql" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="entrypoint.sh">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="PluginBuilder.Dockerfile">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
64
PluginBuilder/PluginSlug.cs
Normal file
64
PluginBuilder/PluginSlug.cs
Normal file
@ -0,0 +1,64 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace PluginBuilder
|
||||
{
|
||||
public record PluginSlug
|
||||
{
|
||||
static Regex SlugRegex = new Regex("^[a-z]{1,}[a-z0-9\\-]{0,}$");
|
||||
public static bool IsValidSlugName(string slug)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(slug);
|
||||
if (!SlugRegex.IsMatch(slug))
|
||||
return false;
|
||||
if (slug[^1] == '-')
|
||||
return false;
|
||||
if (slug.Length > 30)
|
||||
return false;
|
||||
if (slug.Length < 4)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool TryParse(string str, [MaybeNullWhen(false)] out PluginSlug slug)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(str);
|
||||
if (!IsValidSlugName(str))
|
||||
{
|
||||
slug = null;
|
||||
return false;
|
||||
}
|
||||
slug = new PluginSlug(str, false);
|
||||
return true;
|
||||
}
|
||||
public static PluginSlug Parse(string str)
|
||||
{
|
||||
if (TryParse(str, out var slug))
|
||||
return slug;
|
||||
throw new FormatException("Invalid slug name");
|
||||
}
|
||||
public PluginSlug(string slug) : this(slug, true)
|
||||
{ }
|
||||
PluginSlug(string slug, bool check)
|
||||
{
|
||||
if (check)
|
||||
{
|
||||
if (!IsValidSlugName(slug))
|
||||
throw new ArgumentException("Invalid slug name", nameof(slug));
|
||||
}
|
||||
this.slug = slug;
|
||||
}
|
||||
|
||||
|
||||
public static implicit operator PluginSlug(string str)
|
||||
{
|
||||
return new PluginSlug(str);
|
||||
}
|
||||
|
||||
readonly string slug;
|
||||
public override string ToString()
|
||||
{
|
||||
return slug;
|
||||
}
|
||||
}
|
||||
}
|
||||
294
PluginBuilder/ProcessRunner.cs
Normal file
294
PluginBuilder/ProcessRunner.cs
Normal file
@ -0,0 +1,294 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PluginBuilder
|
||||
{
|
||||
public interface IOutputCapture
|
||||
{
|
||||
void AddLine(string line);
|
||||
}
|
||||
public class OutputCapture : IOutputCapture
|
||||
{
|
||||
private readonly List<string> _lines = new List<string>();
|
||||
public IEnumerable<string> Lines => _lines;
|
||||
public void AddLine(string line) => _lines.Add(line);
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Join(Environment.NewLine, _lines);
|
||||
}
|
||||
}
|
||||
public class ProcessSpec
|
||||
{
|
||||
public string? Executable { get; set; }
|
||||
public string? WorkingDirectory { get; set; }
|
||||
public ProcessSpecEnvironmentVariables EnvironmentVariables { get; } = new();
|
||||
|
||||
public IReadOnlyList<string>? Arguments { get; set; }
|
||||
public string? EscapedArguments { get; set; }
|
||||
public IOutputCapture? OutputCapture { get; set; }
|
||||
public IOutputCapture? ErrorCapture { get; set; }
|
||||
|
||||
public DataReceivedEventHandler? OnOutput { get; set; }
|
||||
public DataReceivedEventHandler? OnError { get; set; }
|
||||
public string? Input { get; set; }
|
||||
|
||||
public sealed class ProcessSpecEnvironmentVariables : Dictionary<string, string>
|
||||
{
|
||||
public List<string> DotNetStartupHooks { get; } = new();
|
||||
public List<string> AspNetCoreHostingStartupAssemblies { get; } = new();
|
||||
}
|
||||
}
|
||||
public class ProcessRunner
|
||||
{
|
||||
private static readonly Func<string, string?> _getEnvironmentVariable = static key => Environment.GetEnvironmentVariable(key);
|
||||
|
||||
public ILogger<ProcessRunner> Logger { get; }
|
||||
|
||||
public ProcessRunner(ILogger<ProcessRunner> logger)
|
||||
{
|
||||
Logger = logger;
|
||||
}
|
||||
|
||||
// May not be necessary in the future. See https://github.com/dotnet/corefx/issues/12039
|
||||
public async Task<int> RunAsync(ProcessSpec processSpec, CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(processSpec, nameof(processSpec));
|
||||
|
||||
int exitCode;
|
||||
|
||||
var stopwatch = new Stopwatch();
|
||||
|
||||
using (var process = CreateProcess(processSpec))
|
||||
using (var processState = new ProcessState(process))
|
||||
{
|
||||
cancellationToken.Register(() => processState.TryKill());
|
||||
|
||||
var readOutput = false;
|
||||
var readError = false;
|
||||
if (processSpec.OutputCapture is not null)
|
||||
{
|
||||
readOutput = true;
|
||||
process.OutputDataReceived += (_, a) =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(a.Data))
|
||||
{
|
||||
processSpec.OutputCapture.AddLine(a.Data);
|
||||
}
|
||||
};
|
||||
}
|
||||
else if (processSpec.OnOutput != null)
|
||||
{
|
||||
readOutput = true;
|
||||
process.OutputDataReceived += processSpec.OnOutput;
|
||||
}
|
||||
|
||||
if (processSpec.ErrorCapture is not null)
|
||||
{
|
||||
readError = true;
|
||||
process.ErrorDataReceived += (_, a) =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(a.Data))
|
||||
{
|
||||
processSpec.ErrorCapture.AddLine(a.Data);
|
||||
}
|
||||
};
|
||||
}
|
||||
else if (processSpec.OnError is not null)
|
||||
{
|
||||
readError = true;
|
||||
process.ErrorDataReceived += processSpec.OnError;
|
||||
}
|
||||
|
||||
if (Logger.IsEnabled(LogLevel.Trace))
|
||||
{
|
||||
readOutput = true;
|
||||
readError = true;
|
||||
process.OutputDataReceived += (s, a) =>
|
||||
{
|
||||
// a.Data.EndsWith("\u001b[K")
|
||||
Logger.LogInformation(a.Data);
|
||||
};
|
||||
process.ErrorDataReceived += (s, a) =>
|
||||
{
|
||||
Logger.LogWarning(a.Data);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
stopwatch.Start();
|
||||
process.Start();
|
||||
|
||||
if (readOutput)
|
||||
{
|
||||
process.BeginOutputReadLine();
|
||||
}
|
||||
if (readError)
|
||||
{
|
||||
process.BeginErrorReadLine();
|
||||
}
|
||||
|
||||
if (processSpec.Input is not null)
|
||||
{
|
||||
await process.StandardInput.WriteLineAsync(processSpec.Input);
|
||||
await process.StandardInput.FlushAsync();
|
||||
process.StandardInput.Close();
|
||||
}
|
||||
await processState.Task;
|
||||
|
||||
exitCode = process.ExitCode;
|
||||
stopwatch.Stop();
|
||||
}
|
||||
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
private Process CreateProcess(ProcessSpec processSpec)
|
||||
{
|
||||
var process = new Process
|
||||
{
|
||||
EnableRaisingEvents = true,
|
||||
StartInfo =
|
||||
{
|
||||
FileName = processSpec.Executable,
|
||||
UseShellExecute = false,
|
||||
WorkingDirectory = processSpec.WorkingDirectory,
|
||||
RedirectStandardOutput = processSpec.OutputCapture is not null || (processSpec.OnOutput is not null) || Logger.IsEnabled(LogLevel.Trace),
|
||||
RedirectStandardError = processSpec.ErrorCapture is not null || (processSpec.OnError is not null) || Logger.IsEnabled(LogLevel.Trace),
|
||||
RedirectStandardInput = processSpec.Input is not null
|
||||
}
|
||||
};
|
||||
|
||||
if (processSpec.EscapedArguments is not null)
|
||||
{
|
||||
process.StartInfo.Arguments = processSpec.EscapedArguments;
|
||||
}
|
||||
else if (processSpec.Arguments is not null)
|
||||
{
|
||||
for (var i = 0; i < processSpec.Arguments.Count; i++)
|
||||
{
|
||||
process.StartInfo.ArgumentList.Add(processSpec.Arguments[i]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var env in processSpec.EnvironmentVariables)
|
||||
{
|
||||
process.StartInfo.Environment.Add(env.Key, env.Value);
|
||||
}
|
||||
|
||||
SetEnvironmentVariable(process.StartInfo, "DOTNET_STARTUP_HOOKS", processSpec.EnvironmentVariables.DotNetStartupHooks, Path.PathSeparator, _getEnvironmentVariable);
|
||||
SetEnvironmentVariable(process.StartInfo, "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES", processSpec.EnvironmentVariables.AspNetCoreHostingStartupAssemblies, ';', _getEnvironmentVariable);
|
||||
|
||||
return process;
|
||||
}
|
||||
|
||||
internal static void SetEnvironmentVariable(ProcessStartInfo processStartInfo, string envVarName, List<string> envVarValues, char separator, Func<string, string?> getEnvironmentVariable)
|
||||
{
|
||||
if (envVarValues is { Count: 0 })
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var existing = getEnvironmentVariable(envVarName);
|
||||
if (processStartInfo.Environment.TryGetValue(envVarName, out var value))
|
||||
{
|
||||
existing = CombineEnvironmentVariable(existing, value, separator);
|
||||
}
|
||||
|
||||
string result;
|
||||
if (!string.IsNullOrEmpty(existing))
|
||||
{
|
||||
result = existing + separator + string.Join(separator, envVarValues);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = string.Join(separator, envVarValues);
|
||||
}
|
||||
|
||||
processStartInfo.EnvironmentVariables[envVarName] = result;
|
||||
|
||||
static string? CombineEnvironmentVariable(string? a, string? b, char separator)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(a))
|
||||
{
|
||||
return !string.IsNullOrEmpty(b) ? (a + separator + b) : a;
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
||||
private class ProcessState : IDisposable
|
||||
{
|
||||
private readonly Process _process;
|
||||
private readonly TaskCompletionSource _tcs = new TaskCompletionSource();
|
||||
private volatile bool _disposed;
|
||||
|
||||
public ProcessState(Process process)
|
||||
{
|
||||
_process = process;
|
||||
_process.Exited += OnExited;
|
||||
Task = _tcs.Task.ContinueWith(_ =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// We need to use two WaitForExit calls to ensure that all of the output/events are processed. Previously
|
||||
// this code used Process.Exited, which could result in us missing some output due to the ordering of
|
||||
// events.
|
||||
//
|
||||
// See the remarks here: https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.process.waitforexit#System_Diagnostics_Process_WaitForExit_System_Int32_
|
||||
if (!_process.WaitForExit(Int32.MaxValue))
|
||||
{
|
||||
throw new TimeoutException();
|
||||
}
|
||||
|
||||
_process.WaitForExit();
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
// suppress if this throws if no process is associated with this object anymore.
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Task Task { get; }
|
||||
|
||||
public void TryKill()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (_process is not null && !_process.HasExited)
|
||||
{
|
||||
_process.Kill(entireProcessTree: true);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private void OnExited(object? sender, EventArgs args)
|
||||
=> _tcs.TrySetResult();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
TryKill();
|
||||
_disposed = true;
|
||||
_process.Exited -= OnExited;
|
||||
_process.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
60
PluginBuilder/Program.cs
Normal file
60
PluginBuilder/Program.cs
Normal file
@ -0,0 +1,60 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using PluginBuilder.HostedServices;
|
||||
using PluginBuilder.Services;
|
||||
|
||||
namespace PluginBuilder;
|
||||
|
||||
public class Program
|
||||
{
|
||||
public static Task Main(string[] args)
|
||||
{
|
||||
var host = new Program();
|
||||
return new Program().Start(args);
|
||||
}
|
||||
|
||||
public Task Start(string[]? args = null)
|
||||
{
|
||||
WebApplication app = CreateWebApplication(args);
|
||||
return app.RunAsync();
|
||||
}
|
||||
|
||||
public WebApplication CreateWebApplication(string[]? args = null)
|
||||
{
|
||||
WebApplicationBuilder builder = CreateWebApplicationBuilder(args);
|
||||
var app = builder.Build();
|
||||
Configure(app);
|
||||
return app;
|
||||
}
|
||||
|
||||
public WebApplicationBuilder CreateWebApplicationBuilder(string[]? args = null)
|
||||
{
|
||||
var builder = WebApplication.CreateBuilder(args ?? Array.Empty<string>());
|
||||
builder.Configuration.AddEnvironmentVariables("PB_");
|
||||
AddServices(builder.Configuration, builder.Services);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public void Configure(WebApplication app)
|
||||
{
|
||||
app.UseStaticFiles();
|
||||
app.UseRouting();
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
// app.MapControllerRoute(
|
||||
//name: "default",
|
||||
//pattern: "{controller=Home}/{action=Index}/{id?}");
|
||||
app.MapControllers();
|
||||
}
|
||||
|
||||
public void AddServices(IConfiguration configuration, IServiceCollection services)
|
||||
{
|
||||
services.AddControllersWithViews();
|
||||
services.AddHostedService<DatabaseStartupHostedService>();
|
||||
services.AddHostedService<DockerStartupHostedService>();
|
||||
services.AddHostedService<AzureStartupHostedService>();
|
||||
services.AddSingleton<DBConnectionFactory>();
|
||||
services.AddSingleton<BuildService>();
|
||||
services.AddSingleton<ProcessRunner>();
|
||||
services.AddSingleton<AzureStorageClient>();
|
||||
}
|
||||
}
|
||||
14
PluginBuilder/Properties/launchSettings.json
Normal file
14
PluginBuilder/Properties/launchSettings.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"profiles": {
|
||||
"Debug Profile": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "https://localhost:7259;http://localhost:5001",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development",
|
||||
"PB_POSTGRES": "lo"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
135
PluginBuilder/Services/AzureStorageClient.cs
Normal file
135
PluginBuilder/Services/AzureStorageClient.cs
Normal file
@ -0,0 +1,135 @@
|
||||
using Microsoft.WindowsAzure.Storage;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace PluginBuilder.Services
|
||||
{
|
||||
public class AzureStorageClientException : Exception
|
||||
{
|
||||
public AzureStorageClientException(string message) : base(message)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A wrapper around "az" utility inside a docker image
|
||||
///
|
||||
/// While we could theorically use the Azure Storage library directly instead of this,
|
||||
/// the files to upload on azure are stored in a docker volume, so using the library
|
||||
/// would require us to copy the files to upload out of the docker volume.
|
||||
///
|
||||
/// This wouldn't be ideal, as we would need to make sure to properly clean it up.
|
||||
/// And we also don't have any datadir for this project.
|
||||
///
|
||||
/// Another solution I tried was to directly use fetch the files via MountPoint of the docker volume
|
||||
/// Sadly, on windows docker run on a VM, so the file system isn't local to the machine.
|
||||
/// </summary>
|
||||
public class AzureStorageClient
|
||||
{
|
||||
string scheme;
|
||||
bool isLocalhost;
|
||||
public AzureStorageClient(ProcessRunner processRunner, IConfiguration configuration)
|
||||
{
|
||||
ProcessRunner = processRunner;
|
||||
StorageConnectionString = configuration.GetRequired("STORAGE_CONNECTION_STRING");
|
||||
if (!CloudStorageAccount.TryParse(StorageConnectionString, out var acc))
|
||||
throw new ConfigurationException("STORAGE_CONNECTION_STRING", "Invalid storage connection string");
|
||||
scheme = acc.BlobEndpoint.Scheme;
|
||||
isLocalhost = acc.BlobEndpoint.Host == "localhost" || acc.BlobEndpoint.Host == "127.0.0.1";
|
||||
DefaultContainer = "artifacts";
|
||||
}
|
||||
|
||||
public ProcessRunner ProcessRunner { get; }
|
||||
public string StorageConnectionString { get; }
|
||||
public string DefaultContainer { get; }
|
||||
public async Task<bool> EnsureDefaultContainerExists(CancellationToken cancellationToken = default)
|
||||
{
|
||||
var error = new OutputCapture();
|
||||
var output = new OutputCapture();
|
||||
var code = await ProcessRunner.RunAsync(new ProcessSpec()
|
||||
{
|
||||
Executable = "docker",
|
||||
Arguments = CreateArguments("az", "storage", "container", "create", "--name", DefaultContainer, "--public-access", "blob"),
|
||||
ErrorCapture = error,
|
||||
OutputCapture = output
|
||||
}, cancellationToken);
|
||||
if (code != 0)
|
||||
throw new AzureStorageClientException($"Impossible to create container ({error})");
|
||||
return ToJson(output)["created"]!.Value<bool>();
|
||||
}
|
||||
|
||||
public async Task<string> Upload(string volume, string fileInVolume, string blobName)
|
||||
{
|
||||
var error = new OutputCapture();
|
||||
var output = new OutputCapture();
|
||||
var code = await ProcessRunner.RunAsync(new ProcessSpec()
|
||||
{
|
||||
Executable = "docker",
|
||||
Arguments = CreateArguments(
|
||||
new[]
|
||||
{
|
||||
"-v", $"{volume}:/out"
|
||||
},
|
||||
new[]
|
||||
{
|
||||
"az", "storage", "blob", "upload",
|
||||
"-f", $"/out/{fileInVolume}", "-c", DefaultContainer,
|
||||
"-n", blobName,
|
||||
"--content-type", "application/zip"
|
||||
}),
|
||||
ErrorCapture = error,
|
||||
OutputCapture = output
|
||||
}, default);
|
||||
if (code != 0)
|
||||
throw new AzureStorageClientException($"Impossible to upload ({error})");
|
||||
|
||||
error = new OutputCapture();
|
||||
output = new OutputCapture();
|
||||
code = await ProcessRunner.RunAsync(new ProcessSpec()
|
||||
{
|
||||
Executable = "docker",
|
||||
Arguments = CreateArguments("az", "storage", "blob", "url", "--container-name", DefaultContainer, "--name", blobName, "--protocol", scheme),
|
||||
ErrorCapture = error,
|
||||
OutputCapture = output
|
||||
}, default);
|
||||
if (code != 0)
|
||||
throw new AzureStorageClientException($"Impossible to get the public url of the blob ({error})");
|
||||
return ToString(output);
|
||||
}
|
||||
|
||||
private static JObject ToJson(OutputCapture output)
|
||||
{
|
||||
var txt = output.ToString();
|
||||
// Remove some crap at the end present for god knows why
|
||||
txt = txt.Substring(0, txt.LastIndexOf('}') + 1);
|
||||
return JObject.Parse(txt)!;
|
||||
}
|
||||
private static string ToString(OutputCapture output)
|
||||
{
|
||||
var txt = output.ToString();
|
||||
// Remove some crap at the end present for god knows why
|
||||
txt = txt.Substring(0, txt.LastIndexOf('"') + 1);
|
||||
return JValue.Parse(txt)!.Value<string>()!;
|
||||
}
|
||||
|
||||
private string[] CreateArguments(params string[] args)
|
||||
{
|
||||
return CreateArguments(null, args);
|
||||
}
|
||||
private string[] CreateArguments(string[]? dockerArgs, string[] args)
|
||||
{
|
||||
List<string> a = new List<string>();
|
||||
a.AddRange(new[] { "run", "-ti", "--rm", "--env", $"AZURE_STORAGE_CONNECTION_STRING={StorageConnectionString}" });
|
||||
if (isLocalhost)
|
||||
{
|
||||
// Not needed in prod, but we need it in tests to connect to the azure containers running in docker-compose
|
||||
a.AddRange(new[] { "--network", "host" });
|
||||
}
|
||||
if (dockerArgs is not null)
|
||||
a.AddRange(dockerArgs);
|
||||
a.Add("mcr.microsoft.com/azure-cli:2.9.1");
|
||||
a.AddRange(args);
|
||||
return a.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
139
PluginBuilder/Services/BuildService.cs
Normal file
139
PluginBuilder/Services/BuildService.cs
Normal file
@ -0,0 +1,139 @@
|
||||
using System.Security.Cryptography;
|
||||
using Dapper;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace PluginBuilder.Services
|
||||
{
|
||||
public class BuildServiceException : Exception
|
||||
{
|
||||
public BuildServiceException(string message) : base(message)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
public class BuildService
|
||||
{
|
||||
public BuildService(
|
||||
ILogger<BuildService> logger,
|
||||
ProcessRunner processRunner,
|
||||
DBConnectionFactory connectionFactory,
|
||||
AzureStorageClient azureStorageClient)
|
||||
{
|
||||
Logger = logger;
|
||||
ProcessRunner = processRunner;
|
||||
ConnectionFactory = connectionFactory;
|
||||
AzureStorageClient = azureStorageClient;
|
||||
}
|
||||
|
||||
public ILogger<BuildService> Logger { get; }
|
||||
public ProcessRunner ProcessRunner { get; }
|
||||
public DBConnectionFactory ConnectionFactory { get; }
|
||||
public AzureStorageClient AzureStorageClient { get; }
|
||||
|
||||
public async Task Build(PluginSlug pluginSlug, PluginBuildParameters buildParameters)
|
||||
{
|
||||
var fullBuildId = await CreateNewBuild(pluginSlug);
|
||||
List<string> args = new List<string>();
|
||||
|
||||
// Create the volumes where the artifacts will be stored
|
||||
args.AddRange(new[] { "volume", "create" });
|
||||
args.AddRange(new[] { "--label", $"BTCPAY_PLUGIN_BUILD={fullBuildId}" });
|
||||
var output = new OutputCapture();
|
||||
var code = await ProcessRunner.RunAsync(new ProcessSpec()
|
||||
{
|
||||
Executable = "docker",
|
||||
Arguments = args.ToArray(),
|
||||
OutputCapture = output
|
||||
}, default);
|
||||
if (code != 0)
|
||||
throw new BuildServiceException("docker volume create failed");
|
||||
var volume = output.ToString().Trim();
|
||||
args.Clear();
|
||||
|
||||
// Then let's build by running our image plugin-builder (built in DockerStartupHostedService)
|
||||
var info = new JObject();
|
||||
|
||||
args.Add("run");
|
||||
args.AddRange(new[] { "--env", $"GIT_REPO={buildParameters.GitRepository}" });
|
||||
info["gitRepository"] = buildParameters.GitRepository;
|
||||
info["dockerVolume"] = volume;
|
||||
if (buildParameters.GitRef != null)
|
||||
{
|
||||
args.AddRange(new[] { "--env", $"GIT_REF={buildParameters.GitRef}" });
|
||||
info["gitRef"] = buildParameters.GitRef;
|
||||
}
|
||||
if (buildParameters.PluginDirectory != null)
|
||||
{
|
||||
args.AddRange(new[] { "--env", $"PLUGIN_DIR={buildParameters.PluginDirectory}" });
|
||||
info["pluginDir"] = buildParameters.PluginDirectory;
|
||||
}
|
||||
if (buildParameters.BuildConfig != null)
|
||||
{
|
||||
args.AddRange(new[] { "--env", $"BUILD_CONFIG={buildParameters.BuildConfig}" });
|
||||
info["buildConfig"] = buildParameters.BuildConfig;
|
||||
}
|
||||
|
||||
args.AddRange(new[] { "-v", $"{volume}:/out" });
|
||||
args.AddRange(new[] { "-ti", "--rm" });
|
||||
args.Add("plugin-builder");
|
||||
await UpdateBuild(fullBuildId, "running", info);
|
||||
JObject buildEnv;
|
||||
try
|
||||
{
|
||||
code = await ProcessRunner.RunAsync(new ProcessSpec()
|
||||
{
|
||||
Executable = "docker",
|
||||
Arguments = args.ToArray()
|
||||
}, default);
|
||||
if (code != 0)
|
||||
throw new BuildServiceException("docker build failed");
|
||||
|
||||
string buildEnvStr = await ReadFileInVolume(volume, "build-env.json");
|
||||
buildEnv = JObject.Parse(buildEnvStr);
|
||||
}
|
||||
catch (Exception err)
|
||||
{
|
||||
await UpdateBuild(fullBuildId, "failed", new JObject() { ["error"] = err.Message });
|
||||
throw;
|
||||
}
|
||||
var pluginName = buildEnv["pluginName"]!.Value<string>();
|
||||
string manifestStr = await ReadFileInVolume(volume, $"{pluginName}.btcpay.json");
|
||||
|
||||
var manifest = JObject.Parse(manifestStr);
|
||||
await UpdateBuild(fullBuildId, "waiting-upload", buildEnv, manifest);
|
||||
|
||||
await UpdateBuild(fullBuildId, "uploading", null, null);
|
||||
var url = await AzureStorageClient.Upload(volume, $"{pluginName}.btcpay", $"{fullBuildId}/{pluginName}.btcpay");
|
||||
await UpdateBuild(fullBuildId, "uploaded", new JObject() { ["url"] = url }, null);
|
||||
}
|
||||
|
||||
private async Task<string> ReadFileInVolume(string volume, string file)
|
||||
{
|
||||
var output = new OutputCapture();
|
||||
// Let's read the build-env.json
|
||||
int code = await ProcessRunner.RunAsync(new ProcessSpec()
|
||||
{
|
||||
Executable = "docker",
|
||||
Arguments = new[] {
|
||||
"run", "-ti", "--rm", "-v", $"{volume}:/out", "plugin-builder", "cat", $"/out/{file}" },
|
||||
OutputCapture = output
|
||||
}, default);
|
||||
if (code != 0)
|
||||
throw new BuildServiceException("docker run to read a file in volume");
|
||||
return output.ToString();
|
||||
}
|
||||
|
||||
private async Task UpdateBuild(FullBuildId fullBuildId, string newState, JObject? buildInfo, JObject? manifestInfo = null)
|
||||
{
|
||||
await using var connection = await ConnectionFactory.Open();
|
||||
await connection.UpdateBuild(fullBuildId, newState, buildInfo, manifestInfo);
|
||||
}
|
||||
|
||||
private async Task<FullBuildId> CreateNewBuild(PluginSlug pluginSlug)
|
||||
{
|
||||
await using var connection = await ConnectionFactory.Open();
|
||||
return new FullBuildId(pluginSlug, await connection.NewBuild(pluginSlug));
|
||||
}
|
||||
}
|
||||
}
|
||||
46
PluginBuilder/Services/DBConnectionFactory.cs
Normal file
46
PluginBuilder/Services/DBConnectionFactory.cs
Normal file
@ -0,0 +1,46 @@
|
||||
using Npgsql;
|
||||
|
||||
namespace PluginBuilder.Services
|
||||
{
|
||||
public class DBConnectionFactory
|
||||
{
|
||||
public NpgsqlConnectionStringBuilder ConnectionString { get; }
|
||||
public DBConnectionFactory(IConfiguration config)
|
||||
{
|
||||
try
|
||||
{
|
||||
ConnectionString = new NpgsqlConnectionStringBuilder(config.GetRequired("POSTGRES"));
|
||||
}
|
||||
catch (Exception ex) when (ex is not ConfigurationException)
|
||||
{
|
||||
throw new ConfigurationException("POSTGRES", ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<NpgsqlConnection> Open(CancellationToken cancellationToken = default)
|
||||
{
|
||||
int maxRetries = 10;
|
||||
int retries = maxRetries;
|
||||
retry:
|
||||
var conn = new Npgsql.NpgsqlConnection(ConnectionString.ToString());
|
||||
try
|
||||
{
|
||||
await conn.OpenAsync(cancellationToken);
|
||||
}
|
||||
catch (PostgresException ex) when (ex.IsTransient && retries > 0)
|
||||
{
|
||||
retries--;
|
||||
await conn.DisposeAsync();
|
||||
await Task.Delay((maxRetries - retries) * 100, cancellationToken);
|
||||
goto retry;
|
||||
}
|
||||
catch
|
||||
{
|
||||
conn.Dispose();
|
||||
throw;
|
||||
}
|
||||
return conn;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
33
PluginBuilder/Views/Home/HomePage.cshtml
Normal file
33
PluginBuilder/Views/Home/HomePage.cshtml
Normal file
@ -0,0 +1,33 @@
|
||||
@{
|
||||
Layout = null;
|
||||
}
|
||||
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html lang="en" class="uie" data-theme="light">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link href="~/styles/btcpayserver-variables.css" rel="stylesheet" asp-append-version="true" />
|
||||
<link href="~/styles/btcpayserver-bootstrap.css" rel="stylesheet" asp-append-version="true" />
|
||||
<link href="~/styles/btcpayserver-main.css" rel="stylesheet" asp-append-version="true" />
|
||||
<title>BTCPay Server - Plugin Builder</title>
|
||||
<style>
|
||||
.navigation-logo {
|
||||
display: block;
|
||||
margin-bottom: var(--uie-space-l);
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-height: 120px;
|
||||
min-height: 120px;
|
||||
max-width: 90%;
|
||||
}
|
||||
.navigation-logo-caption {
|
||||
font-size: x-large;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
2
PluginBuilder/Views/_ViewImports.cshtml
Normal file
2
PluginBuilder/Views/_ViewImports.cshtml
Normal file
@ -0,0 +1,2 @@
|
||||
@using PluginBuilder
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
3
PluginBuilder/Views/_ViewStart.cshtml
Normal file
3
PluginBuilder/Views/_ViewStart.cshtml
Normal file
@ -0,0 +1,3 @@
|
||||
@{
|
||||
Layout = "_Layout";
|
||||
}
|
||||
68
PluginBuilder/entrypoint.sh
Normal file
68
PluginBuilder/entrypoint.sh
Normal file
@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
git --version
|
||||
dotnet --info
|
||||
|
||||
: "${BUILD_CONFIG:=Release}"
|
||||
|
||||
BRANCH_OPTS=""
|
||||
[[ "$GIT_REF" ]] && BRANCH_OPTS="-b ${GIT_REF}"
|
||||
|
||||
git clone --depth 1 --recurse-submodules $BRANCH_OPTS --single-branch "${GIT_REPO}" .
|
||||
GIT_COMMIT="$(git rev-parse HEAD)"
|
||||
GIT_COMMIT_DATE=$(git show -s --format=%ci)
|
||||
# To UTC
|
||||
GIT_COMMIT_DATE=$(date -d "$GIT_COMMIT_DATE" --iso-8601=seconds --utc)
|
||||
[[ "$PLUGIN_DIR" ]] && cd "${PLUGIN_DIR}"
|
||||
PLUGIN_NAME="$(ls *.csproj)"
|
||||
PLUGIN_NAME="${PLUGIN_NAME/.csproj/}"
|
||||
dotnet publish -c "${BUILD_CONFIG}" -o "/tmp/publish"
|
||||
|
||||
# PluginPacker crash because of no gpg, but we don't use it anyway...
|
||||
/build-tools/PluginPacker/BTCPayServer.PluginPacker "/tmp/publish" "${PLUGIN_NAME}" "/tmp/publish-package" || true
|
||||
cp /tmp/publish-package/*/*/* /out
|
||||
rm /out/SHA256SUMS.asc /out/SHA256SUMS
|
||||
|
||||
BUILD_DATE=$(date --iso-8601=seconds --utc)
|
||||
# To UTC
|
||||
BUILD_DATE=$(date -d "$BUILD_DATE" --iso-8601=seconds --utc)
|
||||
BUILD_HASH=($(sha256sum /out/${PLUGIN_NAME}.btcpay))
|
||||
|
||||
jq --null-input \
|
||||
--arg buildConfig "$BUILD_CONFIG" \
|
||||
--arg gitRef "$GIT_REF" \
|
||||
--arg gitRepository "$GIT_REPO" \
|
||||
--arg pluginDir "$PLUGIN_DIR" \
|
||||
--arg buildConfig "$BUILD_CONFIG" \
|
||||
--arg gitCommit "$GIT_COMMIT" \
|
||||
--arg gitCommitDate "$GIT_COMMIT_DATE" \
|
||||
--arg buildDate "$BUILD_DATE" \
|
||||
--arg buildHash "$BUILD_HASH" \
|
||||
--arg pluginName "$PLUGIN_NAME" \
|
||||
'{
|
||||
"pluginName": $pluginName,
|
||||
"gitRepository": $gitRepository,
|
||||
"gitRef": $gitRef,
|
||||
"pluginDir": $pluginDir,
|
||||
"buildConfig": $buildConfig,
|
||||
"gitCommit": $gitCommit,
|
||||
"gitCommitDate": $gitCommitDate,
|
||||
"buildDate": $buildDate,
|
||||
"buildHash": $buildHash
|
||||
}' > /out/build-env.json
|
||||
|
||||
|
||||
|
||||
# {
|
||||
# "gitRepository": "https://github.com/Kukks/btcpayserver",
|
||||
# "gitRef": "plugins/collection",
|
||||
# "pluginDir": "Plugins/BTCPayServer.Plugins.AOPP",
|
||||
# "gitCommit": "bed25814a7a47f7bf13b2a1cb9a2dcf544d268dd",
|
||||
# "gitCommitDate": "2022-10-31T10:03:52+00:00",
|
||||
# "buildDate": "2022-11-07T06:10:20+00:00",
|
||||
# "buildHash": "f56cec255e2fc92c1b2c0d39546d87548daa98a7fa0e9f7a7f28f6dc129a31b6"
|
||||
# }
|
||||
|
||||
# ls /out/
|
||||
# BTCPayServer.Plugins.AOPP.btcpay BTCPayServer.Plugins.AOPP.btcpay.json build-env.json
|
||||
BIN
PluginBuilder/wwwroot/favicon.ico
Normal file
BIN
PluginBuilder/wwwroot/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
11320
PluginBuilder/wwwroot/styles/btcpayserver-bootstrap.css
Normal file
11320
PluginBuilder/wwwroot/styles/btcpayserver-bootstrap.css
Normal file
File diff suppressed because it is too large
Load Diff
943
PluginBuilder/wwwroot/styles/btcpayserver-main.css
Normal file
943
PluginBuilder/wwwroot/styles/btcpayserver-main.css
Normal file
@ -0,0 +1,943 @@
|
||||
/* cyrillic-ext */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans Light Italic'), local('OpenSans-LightItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKWyV9hmIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
|
||||
/* cyrillic */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans Light Italic'), local('OpenSans-LightItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKWyV9hvIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
|
||||
/* greek-ext */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans Light Italic'), local('OpenSans-LightItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKWyV9hnIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
|
||||
/* greek */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans Light Italic'), local('OpenSans-LightItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKWyV9hoIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
|
||||
/* vietnamese */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans Light Italic'), local('OpenSans-LightItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKWyV9hkIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
|
||||
/* latin-ext */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans Light Italic'), local('OpenSans-LightItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKWyV9hlIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
/* latin */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans Light Italic'), local('OpenSans-LightItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKWyV9hrIqM.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
/* cyrillic-ext */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Italic'), local('OpenSans-Italic'), url(https://fonts.gstatic.com/s/opensans/v15/mem6YaGs126MiZpBA-UFUK0Udc1UAw.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
|
||||
/* cyrillic */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Italic'), local('OpenSans-Italic'), url(https://fonts.gstatic.com/s/opensans/v15/mem6YaGs126MiZpBA-UFUK0ddc1UAw.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
|
||||
/* greek-ext */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Italic'), local('OpenSans-Italic'), url(https://fonts.gstatic.com/s/opensans/v15/mem6YaGs126MiZpBA-UFUK0Vdc1UAw.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
|
||||
/* greek */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Italic'), local('OpenSans-Italic'), url(https://fonts.gstatic.com/s/opensans/v15/mem6YaGs126MiZpBA-UFUK0adc1UAw.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
|
||||
/* vietnamese */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Italic'), local('OpenSans-Italic'), url(https://fonts.gstatic.com/s/opensans/v15/mem6YaGs126MiZpBA-UFUK0Wdc1UAw.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
|
||||
/* latin-ext */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Italic'), local('OpenSans-Italic'), url(https://fonts.gstatic.com/s/opensans/v15/mem6YaGs126MiZpBA-UFUK0Xdc1UAw.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
/* latin */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Italic'), local('OpenSans-Italic'), url(https://fonts.gstatic.com/s/opensans/v15/mem6YaGs126MiZpBA-UFUK0Zdc0.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
/* cyrillic-ext */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
src: local('Open Sans SemiBold Italic'), local('OpenSans-SemiBoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKXGUdhmIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
|
||||
/* cyrillic */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
src: local('Open Sans SemiBold Italic'), local('OpenSans-SemiBoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKXGUdhvIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
|
||||
/* greek-ext */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
src: local('Open Sans SemiBold Italic'), local('OpenSans-SemiBoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKXGUdhnIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
|
||||
/* greek */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
src: local('Open Sans SemiBold Italic'), local('OpenSans-SemiBoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKXGUdhoIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
|
||||
/* vietnamese */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
src: local('Open Sans SemiBold Italic'), local('OpenSans-SemiBoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKXGUdhkIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
|
||||
/* latin-ext */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
src: local('Open Sans SemiBold Italic'), local('OpenSans-SemiBoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKXGUdhlIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
/* latin */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
src: local('Open Sans SemiBold Italic'), local('OpenSans-SemiBoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKXGUdhrIqM.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
/* cyrillic-ext */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKWiUNhmIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
|
||||
/* cyrillic */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKWiUNhvIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
|
||||
/* greek-ext */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKWiUNhnIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
|
||||
/* greek */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKWiUNhoIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
|
||||
/* vietnamese */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKWiUNhkIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
|
||||
/* latin-ext */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKWiUNhlIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
/* latin */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKWiUNhrIqM.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
/* cyrillic-ext */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 800;
|
||||
src: local('Open Sans ExtraBold Italic'), local('OpenSans-ExtraBoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKW-U9hmIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
|
||||
/* cyrillic */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 800;
|
||||
src: local('Open Sans ExtraBold Italic'), local('OpenSans-ExtraBoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKW-U9hvIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
|
||||
/* greek-ext */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 800;
|
||||
src: local('Open Sans ExtraBold Italic'), local('OpenSans-ExtraBoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKW-U9hnIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
|
||||
/* greek */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 800;
|
||||
src: local('Open Sans ExtraBold Italic'), local('OpenSans-ExtraBoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKW-U9hoIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
|
||||
/* vietnamese */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 800;
|
||||
src: local('Open Sans ExtraBold Italic'), local('OpenSans-ExtraBoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKW-U9hkIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
|
||||
/* latin-ext */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 800;
|
||||
src: local('Open Sans ExtraBold Italic'), local('OpenSans-ExtraBoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKW-U9hlIqOjjg.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
/* latin */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 800;
|
||||
src: local('Open Sans ExtraBold Italic'), local('OpenSans-ExtraBoldItalic'), url(https://fonts.gstatic.com/s/opensans/v15/memnYaGs126MiZpBA-UFUKW-U9hrIqM.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
/* cyrillic-ext */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans Light'), local('OpenSans-Light'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN_r8OX-hpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
|
||||
/* cyrillic */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans Light'), local('OpenSans-Light'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN_r8OVuhpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
|
||||
/* greek-ext */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans Light'), local('OpenSans-Light'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN_r8OXuhpOqc.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
|
||||
/* greek */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans Light'), local('OpenSans-Light'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN_r8OUehpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
|
||||
/* vietnamese */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans Light'), local('OpenSans-Light'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN_r8OXehpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
|
||||
/* latin-ext */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans Light'), local('OpenSans-Light'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN_r8OXOhpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
/* latin */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans Light'), local('OpenSans-Light'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN_r8OUuhp.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
/* cyrillic-ext */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Regular'), local('OpenSans-Regular'), url(https://fonts.gstatic.com/s/opensans/v15/mem8YaGs126MiZpBA-UFWJ0bbck.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
|
||||
/* cyrillic */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Regular'), local('OpenSans-Regular'), url(https://fonts.gstatic.com/s/opensans/v15/mem8YaGs126MiZpBA-UFUZ0bbck.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
|
||||
/* greek-ext */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Regular'), local('OpenSans-Regular'), url(https://fonts.gstatic.com/s/opensans/v15/mem8YaGs126MiZpBA-UFWZ0bbck.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
|
||||
/* greek */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Regular'), local('OpenSans-Regular'), url(https://fonts.gstatic.com/s/opensans/v15/mem8YaGs126MiZpBA-UFVp0bbck.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
|
||||
/* vietnamese */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Regular'), local('OpenSans-Regular'), url(https://fonts.gstatic.com/s/opensans/v15/mem8YaGs126MiZpBA-UFWp0bbck.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
|
||||
/* latin-ext */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Regular'), local('OpenSans-Regular'), url(https://fonts.gstatic.com/s/opensans/v15/mem8YaGs126MiZpBA-UFW50bbck.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
/* latin */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Regular'), local('OpenSans-Regular'), url(https://fonts.gstatic.com/s/opensans/v15/mem8YaGs126MiZpBA-UFVZ0b.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
/* cyrillic-ext */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local('Open Sans SemiBold'), local('OpenSans-SemiBold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UNirkOX-hpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
|
||||
/* cyrillic */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local('Open Sans SemiBold'), local('OpenSans-SemiBold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UNirkOVuhpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
|
||||
/* greek-ext */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local('Open Sans SemiBold'), local('OpenSans-SemiBold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UNirkOXuhpOqc.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
|
||||
/* greek */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local('Open Sans SemiBold'), local('OpenSans-SemiBold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UNirkOUehpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
|
||||
/* vietnamese */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local('Open Sans SemiBold'), local('OpenSans-SemiBold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UNirkOXehpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
|
||||
/* latin-ext */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local('Open Sans SemiBold'), local('OpenSans-SemiBold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UNirkOXOhpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
/* latin */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local('Open Sans SemiBold'), local('OpenSans-SemiBold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UNirkOUuhp.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
/* cyrillic-ext */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold'), local('OpenSans-Bold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN7rgOX-hpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
|
||||
/* cyrillic */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold'), local('OpenSans-Bold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN7rgOVuhpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
|
||||
/* greek-ext */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold'), local('OpenSans-Bold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN7rgOXuhpOqc.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
|
||||
/* greek */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold'), local('OpenSans-Bold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN7rgOUehpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
|
||||
/* vietnamese */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold'), local('OpenSans-Bold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN7rgOXehpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
|
||||
/* latin-ext */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold'), local('OpenSans-Bold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN7rgOXOhpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
/* latin */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold'), local('OpenSans-Bold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN7rgOUuhp.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
/* cyrillic-ext */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
src: local('Open Sans ExtraBold'), local('OpenSans-ExtraBold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN8rsOX-hpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
|
||||
/* cyrillic */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
src: local('Open Sans ExtraBold'), local('OpenSans-ExtraBold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN8rsOVuhpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
|
||||
/* greek-ext */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
src: local('Open Sans ExtraBold'), local('OpenSans-ExtraBold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN8rsOXuhpOqc.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
|
||||
/* greek */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
src: local('Open Sans ExtraBold'), local('OpenSans-ExtraBold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN8rsOUehpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
|
||||
/* vietnamese */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
src: local('Open Sans ExtraBold'), local('OpenSans-ExtraBold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN8rsOXehpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
|
||||
/* latin-ext */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
src: local('Open Sans ExtraBold'), local('OpenSans-ExtraBold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN8rsOXOhpOqc.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
/* latin */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
src: local('Open Sans ExtraBold'), local('OpenSans-ExtraBold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN8rsOUuhp.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
* {
|
||||
-webkit-animation: none !important;
|
||||
animation: none !important;
|
||||
transition: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--btcpay-font-family-base);
|
||||
font-size: var(--btcpay-font-size-base);
|
||||
font-weight: var(--btcpay-font-weight-normal);
|
||||
line-height: 1.6;
|
||||
color: var(--btcpay-body-text);
|
||||
background-color: var(--btcpay-body-bg);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: var(--btcpay-font-family-monospace);
|
||||
border-radius: var(--btcpay-border-radius);
|
||||
padding: var(--btcpay-space-xs);
|
||||
color: var(--btcpay-code-text);
|
||||
background-color: var(--btcpay-code-bg);
|
||||
}
|
||||
|
||||
pre {
|
||||
font-family: var(--btcpay-font-family-monospace);
|
||||
border-radius: var(--btcpay-border-radius);
|
||||
color: var(--btcpay-pre-text);
|
||||
background-color: var(--btcpay-pre-bg);
|
||||
}
|
||||
|
||||
pre code {
|
||||
padding: var(--btcpay-space-m) !important;
|
||||
background: var(--btcpay-pre-bg);
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--btcpay-body-link);
|
||||
}
|
||||
|
||||
a:focus,
|
||||
a:hover {
|
||||
color: var(--btcpay-body-link-accent);
|
||||
}
|
||||
|
||||
/* components */
|
||||
|
||||
.btcpay-header {
|
||||
color: var(--btcpay-header-text);
|
||||
background-color: var(--btcpay-header-bg);
|
||||
}
|
||||
|
||||
.btcpay-header a {
|
||||
color: var(--btcpay-header-link);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btcpay-header a:focus,
|
||||
.btcpay-header a:hover {
|
||||
color: var(--btcpay-header-link-accent);
|
||||
}
|
||||
|
||||
.btcpay-header-columns {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.btcpay-header-logo {
|
||||
height: 45px;
|
||||
}
|
||||
|
||||
.btcpay-pills input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.btcpay-pills label,
|
||||
.btcpay-pill {
|
||||
display: inline-block;
|
||||
padding: var(--btcpay-space-s) 1.5rem;
|
||||
color: var(--btcpay-body-link);
|
||||
background: transparent;
|
||||
font-weight: var(--btcpay-font-weight-semibold);
|
||||
margin-right: var(--btcpay-space-m);
|
||||
border: 1px solid var(--btcpay-secondary-border);
|
||||
cursor: pointer;
|
||||
border-radius: 5rem;
|
||||
text-decoration: none;
|
||||
transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
|
||||
}
|
||||
|
||||
.btcpay-pills input:not(:checked):not([disabled]) + label:hover,
|
||||
.btcpay-pill:hover {
|
||||
color: var(--btcpay-body-link);
|
||||
border-color: var(--btcpay-secondary-border-hover);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btcpay-pills input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.btcpay-pills input:checked + label,
|
||||
.btcpay-pill.active {
|
||||
color: var(--btcpay-body-text-active);
|
||||
background: var(--btcpay-body-bg-active);
|
||||
border-color: var(--btcpay-body-bg-active);
|
||||
}
|
||||
|
||||
.btcpay-pills input[disabled] + label,
|
||||
.btcpay-pill.disabled {
|
||||
color: var(--btcpay-body-text-muted) !important;
|
||||
border-color: var(--btcpay-secondary-border) !important;
|
||||
opacity: .5 !important;
|
||||
}
|
||||
|
||||
.btcpay-theme-switch {
|
||||
--btcpay-theme-switch-light-color: var(--btcpay-white);
|
||||
--btcpay-theme-switch-dark-color: var(--btcpay-neutral-900);
|
||||
--btcpay-theme-switch-icon-size: 1.25rem;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--btcpay-white);
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.btcpay-theme-switch svg {
|
||||
height: var(--btcpay-theme-switch-icon-size);
|
||||
width: var(--btcpay-theme-switch-icon-size);
|
||||
}
|
||||
|
||||
.btcpay-theme-switch path {
|
||||
stroke-width: .5px;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.btcpay-theme-switch:hover .btcpay-theme-switch-light, .btcpay-theme-switch:focus .btcpay-theme-switch-light {
|
||||
fill: var(--btcpay-theme-switch-light-color);
|
||||
}
|
||||
|
||||
.btcpay-theme-switch:hover .btcpay-theme-switch-dark, .btcpay-theme-switch:focus .btcpay-theme-switch-dark {
|
||||
fill: var(--btcpay-theme-switch-dark-color);
|
||||
}
|
||||
|
||||
.btcpay-theme-switch-dark {
|
||||
display: inline-block;
|
||||
stroke: var(--btcpay-theme-switch-dark-color);
|
||||
}
|
||||
|
||||
[data-btcpay-theme="dark"]:root .btcpay-theme-switch-dark {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
|
||||
:root:not([data-btcpay-theme="dark"]) .btcpay-theme-switch-dark {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.btcpay-theme-switch-light {
|
||||
display: none;
|
||||
stroke: var(--btcpay-theme-switch-light-color);
|
||||
}
|
||||
|
||||
[data-btcpay-theme="dark"]:root .btcpay-theme-switch-light {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
|
||||
:root:not([data-btcpay-theme="light"]) .btcpay-theme-switch-light {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.btcpay-footer {
|
||||
color: var(--btcpay-footer-text);
|
||||
background-color: var(--btcpay-footer-bg);
|
||||
}
|
||||
|
||||
.btcpay-footer a {
|
||||
color: var(--btcpay-footer-link);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btcpay-footer a:focus,
|
||||
.btcpay-footer a:hover {
|
||||
color: var(--btcpay-footer-link-accent);
|
||||
}
|
||||
|
||||
.btcpay-footer-columns {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.btcpay-footer-columns > * {
|
||||
flex: 1 1 100%;
|
||||
margin-bottom: var(--btcpay-space-m);
|
||||
}
|
||||
|
||||
.btcpay-footer-columns h4,
|
||||
.btcpay-footer-columns h5,
|
||||
.btcpay-footer-columns h6 {
|
||||
margin: var(--btcpay-space-m) 0;
|
||||
font-weight: var(--btcpay-font-weight-bold);
|
||||
}
|
||||
|
||||
.btcpay-footer-columns ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.btcpay-footer-columns ul li {
|
||||
padding: var(--btcpay-space-xs) 0;
|
||||
}
|
||||
|
||||
@media (min-width: 576px) {
|
||||
.btcpay-footer-columns > * {
|
||||
flex: 1;
|
||||
}
|
||||
.btcpay-footer-columns > * + * {
|
||||
margin-left: var(--btcpay-space-m);
|
||||
}
|
||||
}
|
||||
578
PluginBuilder/wwwroot/styles/btcpayserver-variables.css
Normal file
578
PluginBuilder/wwwroot/styles/btcpayserver-variables.css
Normal file
@ -0,0 +1,578 @@
|
||||
:root {
|
||||
--btcpay-border-radius: .25rem;
|
||||
--btcpay-border-radius-l: .5rem;
|
||||
--btcpay-border-radius-xl: 1rem;
|
||||
--btcpay-border-radius-xxl: 1.5rem;
|
||||
--btcpay-transition-duration-fast: .2s;
|
||||
--btcpay-transition-duration-default: .3s;
|
||||
--btcpay-transition-duration-slow: .5s;
|
||||
--btcpay-brand-primary: #51b13e;
|
||||
--btcpay-brand-secondary: #CEDC21;
|
||||
--btcpay-brand-tertiary: #1e7a44;
|
||||
--btcpay-brand-dark: #0F3B21;
|
||||
--btcpay-white: #ffffff;
|
||||
--btcpay-white-rgb: 255, 255, 255;
|
||||
--btcpay-black: #000000;
|
||||
--btcpay-black-rgb: 0, 0, 0;
|
||||
--btcpay-neutral-light-100: #f8f9fa;
|
||||
--btcpay-neutral-light-200: #e9ecef;
|
||||
--btcpay-neutral-light-300: #dee2e6;
|
||||
--btcpay-neutral-light-400: #ced4da;
|
||||
--btcpay-neutral-light-500: #8f979e;
|
||||
--btcpay-neutral-light-600: #6c757d;
|
||||
--btcpay-neutral-light-700: #495057;
|
||||
--btcpay-neutral-light-800: #343a40;
|
||||
--btcpay-neutral-light-900: #292929;
|
||||
--btcpay-neutral-light-rgb: 143,151,158;
|
||||
--btcpay-neutral-dark-100: #F0F6FC;
|
||||
--btcpay-neutral-dark-200: #C9D1D9;
|
||||
--btcpay-neutral-dark-300: #B1BAC4;
|
||||
--btcpay-neutral-dark-400: #8B949E;
|
||||
--btcpay-neutral-dark-500: #6E7681;
|
||||
--btcpay-neutral-dark-600: #484F58;
|
||||
--btcpay-neutral-dark-700: #30363D;
|
||||
--btcpay-neutral-dark-800: #21262D;
|
||||
--btcpay-neutral-dark-900: #161B22;
|
||||
--btcpay-neutral-dark-rgb: 110,118,129;
|
||||
--btcpay-primary-100: #c7e6c1;
|
||||
--btcpay-primary-200: #b5dead;
|
||||
--btcpay-primary-300: #9dd392;
|
||||
--btcpay-primary-400: #7cc46e;
|
||||
--btcpay-primary-500: #44a431;
|
||||
--btcpay-primary-600: #389725;
|
||||
--btcpay-primary-700: #2e8a1b;
|
||||
--btcpay-primary-800: #247d12;
|
||||
--btcpay-primary-900: #1c710b;
|
||||
--btcpay-primary-rgb: 68,164,49;
|
||||
--btcpay-green-100: #EEFAEB;
|
||||
--btcpay-green-200: #C7E8C0;
|
||||
--btcpay-green-300: #A0D695;
|
||||
--btcpay-green-400: #78C369;
|
||||
--btcpay-green-500: #51B13E;
|
||||
--btcpay-green-600: #419437;
|
||||
--btcpay-green-700: #307630;
|
||||
--btcpay-green-800: #205928;
|
||||
--btcpay-green-900: #0F3B21;
|
||||
--btcpay-green-rgb: 81,177,62;
|
||||
--btcpay-blue-100: #b5e1e8;
|
||||
--btcpay-blue-200: #9dd7e1;
|
||||
--btcpay-blue-300: #7ccad7;
|
||||
--btcpay-blue-400: #51b9c9;
|
||||
--btcpay-blue-500: #17a2b8;
|
||||
--btcpay-blue-600: #03899e;
|
||||
--btcpay-blue-700: #007d91;
|
||||
--btcpay-blue-800: #007284;
|
||||
--btcpay-blue-900: #006778;
|
||||
--btcpay-blue-rgb: 23,162,184;
|
||||
--btcpay-yellow-100: #FFFAF0;
|
||||
--btcpay-yellow-200: #FFF2D9;
|
||||
--btcpay-yellow-300: #FFE3AC;
|
||||
--btcpay-yellow-400: #FFCF70;
|
||||
--btcpay-yellow-500: #FFC043;
|
||||
--btcpay-yellow-600: #BC8B2C;
|
||||
--btcpay-yellow-700: #997328;
|
||||
--btcpay-yellow-800: #674D1B;
|
||||
--btcpay-yellow-900: #543D10;
|
||||
--btcpay-yellow-rgb: 255,192,67;
|
||||
--btcpay-red-100: #FFEFED;
|
||||
--btcpay-red-200: #FED7D2;
|
||||
--btcpay-red-300: #F1998E;
|
||||
--btcpay-red-400: #E85C4A;
|
||||
--btcpay-red-500: #E11900;
|
||||
--btcpay-red-600: #AB1300;
|
||||
--btcpay-red-700: #870F00;
|
||||
--btcpay-red-800: #5A0A00;
|
||||
--btcpay-red-900: #420105;
|
||||
--btcpay-red-rgb: 225,25,0;
|
||||
--btcpay-purple-100: #F4F1FA;
|
||||
--btcpay-purple-200: #E3DDF2;
|
||||
--btcpay-purple-300: #C1B5E3;
|
||||
--btcpay-purple-400: #957FCE;
|
||||
--btcpay-purple-500: #7356BF;
|
||||
--btcpay-purple-600: #574191;
|
||||
--btcpay-purple-700: #453473;
|
||||
--btcpay-purple-800: #2E224C;
|
||||
--btcpay-purple-900: #1A1033;
|
||||
--btcpay-purple-rgb: 115,86,191;
|
||||
--btcpay-orange-100: #FFF3EF;
|
||||
--btcpay-orange-200: #FFE1D6;
|
||||
--btcpay-orange-300: #FABDA5;
|
||||
--btcpay-orange-400: #FA9269;
|
||||
--btcpay-orange-500: #FF6937;
|
||||
--btcpay-orange-600: #C14F29;
|
||||
--btcpay-orange-700: #9A3F21;
|
||||
--btcpay-orange-800: #672A16;
|
||||
--btcpay-orange-900: #3D1300;
|
||||
--btcpay-orange-rgb: 255,105,55;
|
||||
--btcpay-brown-100: #F6F0EA;
|
||||
--btcpay-brown-200: #EBE0DB;
|
||||
--btcpay-brown-300: #D2BBB0;
|
||||
--btcpay-brown-400: #B18977;
|
||||
--btcpay-brown-500: #99644C;
|
||||
--btcpay-brown-600: #744C3A;
|
||||
--btcpay-brown-700: #5C3C2E;
|
||||
--btcpay-brown-800: #3D281E;
|
||||
--btcpay-brown-900: #241914;
|
||||
--btcpay-brown-rgb: 153,100,76;
|
||||
--btcpay-pink-100: #FFEDF9;
|
||||
--btcpay-pink-200: #FFCEE5;
|
||||
--btcpay-pink-300: #FFACD6;
|
||||
--btcpay-pink-400: #FE82C2;
|
||||
--btcpay-pink-500: #F162AF;
|
||||
--btcpay-pink-600: #BB4183;
|
||||
--btcpay-pink-700: #7D2457;
|
||||
--btcpay-pink-800: #5E103E;
|
||||
--btcpay-pink-900: #4A042E;
|
||||
--btcpay-pink-rgb: 241,98,175;
|
||||
--btcpay-space-xs: 4px;
|
||||
--btcpay-space-s: 8px;
|
||||
--btcpay-space-m: 16px;
|
||||
--btcpay-space-l: 32px;
|
||||
--btcpay-space-xl: 64px;
|
||||
--btcpay-space-xxl: 80px;
|
||||
--btcpay-font-family-base: "Open Sans", "Helvetica Neue", Arial, sans-serif;
|
||||
--btcpay-font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
--btcpay-font-size-xs: 10px;
|
||||
--btcpay-font-size-s: 12px;
|
||||
--btcpay-font-size-m: 14px;
|
||||
--btcpay-font-size-l: 18px;
|
||||
--btcpay-font-size-xl: 36px;
|
||||
--btcpay-font-size-xxl: 45px;
|
||||
--btcpay-font-weight-normal: 400;
|
||||
--btcpay-font-weight-semibold: 600;
|
||||
--btcpay-font-weight-bold: 700;
|
||||
--btcpay-neutral-100: var(--btcpay-neutral-light-100);
|
||||
--btcpay-neutral-200: var(--btcpay-neutral-light-200);
|
||||
--btcpay-neutral-300: var(--btcpay-neutral-light-300);
|
||||
--btcpay-neutral-400: var(--btcpay-neutral-light-400);
|
||||
--btcpay-neutral-500: var(--btcpay-neutral-light-500);
|
||||
--btcpay-neutral-600: var(--btcpay-neutral-light-600);
|
||||
--btcpay-neutral-700: var(--btcpay-neutral-light-700);
|
||||
--btcpay-neutral-800: var(--btcpay-neutral-light-800);
|
||||
--btcpay-neutral-900: var(--btcpay-neutral-light-900);
|
||||
--btcpay-font-size-base: var(--btcpay-font-size-m);
|
||||
--btcpay-bg-tile: var(--btcpay-white);
|
||||
--btcpay-bg-dark: var(--btcpay-brand-dark);
|
||||
--btcpay-body-bg: var(--btcpay-neutral-100);
|
||||
--btcpay-body-bg-light: var(--btcpay-white);
|
||||
--btcpay-body-bg-medium: var(--btcpay-neutral-200);
|
||||
--btcpay-body-bg-striped: var(--btcpay-neutral-200);
|
||||
--btcpay-body-bg-hover: var(--btcpay-white);
|
||||
--btcpay-body-bg-active: var(--btcpay-primary);
|
||||
--btcpay-body-bg-rgb: 248, 249, 250;
|
||||
--btcpay-body-border-light: var(--btcpay-neutral-200);
|
||||
--btcpay-body-border-medium: var(--btcpay-neutral-300);
|
||||
--btcpay-body-text: var(--btcpay-neutral-900);
|
||||
--btcpay-body-text-striped: var(--btcpay-body-text);
|
||||
--btcpay-body-text-hover: var(--btcpay-body-text);
|
||||
--btcpay-body-text-active: var(--btcpay-white);
|
||||
--btcpay-body-text-muted: var(--btcpay-neutral-500);
|
||||
--btcpay-body-text-rgb: 41, 41, 41;
|
||||
--btcpay-body-link: var(--btcpay-primary);
|
||||
--btcpay-body-link-accent: var(--btcpay-primary-accent);
|
||||
--btcpay-body-shadow: rgba(25, 135, 84, 0.33);
|
||||
--btcpay-wizard-bg: var(--btcpay-body-bg);
|
||||
--btcpay-wizard-text: var(--btcpay-body-text);
|
||||
--btcpay-header-bg: var(--btcpay-white);
|
||||
--btcpay-header-text: var(--btcpay-body-text);
|
||||
--btcpay-header-link: var(--btcpay-header-text);
|
||||
--btcpay-header-link-accent: var(--btcpay-primary);
|
||||
--btcpay-header-link-active: var(--btcpay-primary);
|
||||
--btcpay-nav-link: var(--btcpay-neutral-600);
|
||||
--btcpay-nav-link-accent: var(--btcpay-neutral-700);
|
||||
--btcpay-nav-link-active: var(--btcpay-neutral-900);
|
||||
--btcpay-nav-bg: transparent;
|
||||
--btcpay-nav-bg-hover: transparent;
|
||||
--btcpay-nav-bg-active: transparent;
|
||||
--btcpay-nav-border: transparent;
|
||||
--btcpay-nav-border-hover: transparent;
|
||||
--btcpay-nav-border-active: var(--btcpay-primary);
|
||||
--btcpay-form-bg: var(--btcpay-white);
|
||||
--btcpay-form-bg-hover: var(--btcpay-form-bg);
|
||||
--btcpay-form-bg-addon: var(--btcpay-neutral-300);
|
||||
--btcpay-form-bg-disabled: var(--btcpay-neutral-200);
|
||||
--btcpay-form-text: var(--btcpay-neutral-900);
|
||||
--btcpay-form-text-label: var(--btcpay-neutral-700);
|
||||
--btcpay-form-text-addon: var(--btcpay-neutral-700);
|
||||
--btcpay-form-border: var(--btcpay-neutral-300);
|
||||
--btcpay-form-border-check: var(--btcpay-neutral-400);
|
||||
--btcpay-form-border-hover: var(--btcpay-primary);
|
||||
--btcpay-form-border-focus: var(--btcpay-primary);
|
||||
--btcpay-form-border-active: var(--btcpay-form-border);
|
||||
--btcpay-form-border-disabled: var(--btcpay-form-border);
|
||||
--btcpay-form-shadow-size: 2px;
|
||||
--btcpay-form-shadow-focus: var(--btcpay-primary-shadow);
|
||||
--btcpay-form-shadow-valid: var(--btcpay-success-shadow);
|
||||
--btcpay-form-shadow-invalid: var(--btcpay-danger-shadow);
|
||||
--btcpay-toggle-bg: var(--btcpay-neutral-500);
|
||||
--btcpay-toggle-bg-hover: var(--btcpay-neutral-600);
|
||||
--btcpay-toggle-bg-active: var(--btcpay-primary);
|
||||
--btcpay-toggle-bg-active-hover: var(--btcpay-primary-600);
|
||||
--btcpay-footer-bg: var(--btcpay-body-bg);
|
||||
--btcpay-footer-text: var(--btcpay-neutral-500);
|
||||
--btcpay-footer-link: var(--btcpay-neutral-500);
|
||||
--btcpay-footer-link-accent: var(--btcpay-neutral-600);
|
||||
--btcpay-code-text: var(--btcpay-body-text);
|
||||
--btcpay-code-bg: transparent;
|
||||
--btcpay-pre-text: var(--btcpay-white);
|
||||
--btcpay-pre-bg: var(--btcpay-neutral-900);
|
||||
--btcpay-primary: var(--btcpay-brand-primary);
|
||||
--btcpay-primary-accent: var(--btcpay-brand-tertiary);
|
||||
--btcpay-primary-text: var(--btcpay-white);
|
||||
--btcpay-primary-text-hover: var(--btcpay-white);
|
||||
--btcpay-primary-text-active: var(--btcpay-white);
|
||||
--btcpay-primary-bg-hover: var(--btcpay-primary-accent);
|
||||
--btcpay-primary-bg-active: var(--btcpay-primary-accent);
|
||||
--btcpay-primary-border: var(--btcpay-primary);
|
||||
--btcpay-primary-border-hover: var(--btcpay-primary-bg-hover);
|
||||
--btcpay-primary-border-active: var(--btcpay-primary-bg-active);
|
||||
--btcpay-primary-dim-bg: var(--btcpay-primary-500);
|
||||
--btcpay-primary-dim-bg-striped: var(--btcpay-primary-400);
|
||||
--btcpay-primary-dim-bg-hover: var(--btcpay-primary-600);
|
||||
--btcpay-primary-dim-bg-active: var(--btcpay-primary-700);
|
||||
--btcpay-primary-dim-border: var(--btcpay-primary-dim-bg);
|
||||
--btcpay-primary-dim-border-active: var(--btcpay-primary-dim-bg-active);
|
||||
--btcpay-primary-dim-text: var(--btcpay-white);
|
||||
--btcpay-primary-dim-text-striped: var(--btcpay-primary-dim-text);
|
||||
--btcpay-primary-dim-text-hover: var(--btcpay-primary-dim-text);
|
||||
--btcpay-primary-dim-text-active: var(--btcpay-primary-900);
|
||||
--btcpay-primary-shadow: rgba(81, 177, 62, 0.5);
|
||||
--btcpay-primary-rgb: 81, 177, 62;
|
||||
--btcpay-secondary: var(--btcpay-white);
|
||||
--btcpay-secondary-accent: var(--btcpay-secondary);
|
||||
--btcpay-secondary-text: var(--btcpay-primary);
|
||||
--btcpay-secondary-text-hover: var(--btcpay-primary);
|
||||
--btcpay-secondary-text-active: var(--btcpay-brand-dark);
|
||||
--btcpay-secondary-bg-hover: var(--btcpay-secondary-accent);
|
||||
--btcpay-secondary-bg-active: var(--btcpay-secondary-accent);
|
||||
--btcpay-secondary-border: var(--btcpay-neutral-300);
|
||||
--btcpay-secondary-border-hover: var(--btcpay-primary);
|
||||
--btcpay-secondary-border-active: var(--btcpay-neutral-500);
|
||||
--btcpay-secondary-dim-bg: var(--btcpay-neutral-200);
|
||||
--btcpay-secondary-dim-bg-striped: var(--btcpay-neutral-300);
|
||||
--btcpay-secondary-dim-bg-hover: var(--btcpay-neutral-300);
|
||||
--btcpay-secondary-dim-bg-active: var(--btcpay-neutral-400);
|
||||
--btcpay-secondary-dim-border: var(--btcpay-secondary-dim-bg);
|
||||
--btcpay-secondary-dim-border-active: var(--btcpay-secondary-dim-bg-active);
|
||||
--btcpay-secondary-dim-text: var(--btcpay-neutral-800);
|
||||
--btcpay-secondary-dim-text-striped: var(--btcpay-secondary-dim-text);
|
||||
--btcpay-secondary-dim-text-hover: var(--btcpay-secondary-dim-text);
|
||||
--btcpay-secondary-dim-text-active: var(--btcpay-neutral-900);
|
||||
--btcpay-secondary-shadow: rgba(130, 138, 145, 0.33);
|
||||
--btcpay-secondary-rgb: 255, 255, 255;
|
||||
--btcpay-success: var(--btcpay-green-500);
|
||||
--btcpay-success-accent: var(--btcpay-green-700);
|
||||
--btcpay-success-text: var(--btcpay-white);
|
||||
--btcpay-success-text-hover: var(--btcpay-white);
|
||||
--btcpay-success-text-active: var(--btcpay-white);
|
||||
--btcpay-success-bg-hover: var(--btcpay-success-accent);
|
||||
--btcpay-success-bg-active: var(--btcpay-success-accent);
|
||||
--btcpay-success-border: var(--btcpay-success);
|
||||
--btcpay-success-border-hover: var(--btcpay-success-bg-hover);
|
||||
--btcpay-success-border-active: var(--btcpay-success-bg-active);
|
||||
--btcpay-success-dim-bg: var(--btcpay-green-100);
|
||||
--btcpay-success-dim-bg-striped: var(--btcpay-green-200);
|
||||
--btcpay-success-dim-bg-hover: var(--btcpay-green-200);
|
||||
--btcpay-success-dim-bg-active: var(--btcpay-green-300);
|
||||
--btcpay-success-dim-border: var(--btcpay-success-dim-bg);
|
||||
--btcpay-success-dim-border-active: var(--btcpay-success-dim-bg-active);
|
||||
--btcpay-success-dim-text: var(--btcpay-green-800);
|
||||
--btcpay-success-dim-text-striped: var(--btcpay-success-dim-text);
|
||||
--btcpay-success-dim-text-hover: var(--btcpay-success-dim-text);
|
||||
--btcpay-success-dim-text-active: var(--btcpay-green-900);
|
||||
--btcpay-success-shadow: rgba(60, 153, 110, 0.33);
|
||||
--btcpay-success-rgb: var(--btcpay-green-rgb);
|
||||
--btcpay-info: var(--btcpay-blue-500);
|
||||
--btcpay-info-accent: var(--btcpay-blue-700);
|
||||
--btcpay-info-text: var(--btcpay-white);
|
||||
--btcpay-info-text-hover: var(--btcpay-white);
|
||||
--btcpay-info-text-active: var(--btcpay-white);
|
||||
--btcpay-info-bg-hover: var(--btcpay-info-accent);
|
||||
--btcpay-info-bg-active: var(--btcpay-info-accent);
|
||||
--btcpay-info-border: var(--btcpay-info);
|
||||
--btcpay-info-border-hover: var(--btcpay-info-bg-hover);
|
||||
--btcpay-info-border-active: var(--btcpay-info-bg-active);
|
||||
--btcpay-info-dim-bg: var(--btcpay-blue-100);
|
||||
--btcpay-info-dim-bg-striped: var(--btcpay-blue-200);
|
||||
--btcpay-info-dim-bg-hover: var(--btcpay-blue-200);
|
||||
--btcpay-info-dim-bg-active: var(--btcpay-blue-300);
|
||||
--btcpay-info-dim-border: var(--btcpay-info-dim-bg);
|
||||
--btcpay-info-dim-border-active: var(--btcpay-info-dim-bg-active);
|
||||
--btcpay-info-dim-text: var(--btcpay-blue-800);
|
||||
--btcpay-info-dim-text-striped: var(--btcpay-info-dim-text);
|
||||
--btcpay-info-dim-text-hover: var(--btcpay-info-dim-text);
|
||||
--btcpay-info-dim-text-active: var(--btcpay-blue-900);
|
||||
--btcpay-info-shadow: rgba(11, 172, 204, 0.33);
|
||||
--btcpay-info-rgb: var(--btcpay-blue-rgb);
|
||||
--btcpay-warning: var(--btcpay-yellow-500);
|
||||
--btcpay-warning-accent: var(--btcpay-yellow-700);
|
||||
--btcpay-warning-text: var(--btcpay-neutral-900);
|
||||
--btcpay-warning-text-hover: var(--btcpay-neutral-900);
|
||||
--btcpay-warning-text-active: var(--btcpay-neutral-900);
|
||||
--btcpay-warning-bg-hover: var(--btcpay-warning-accent);
|
||||
--btcpay-warning-bg-active: var(--btcpay-warning-accent);
|
||||
--btcpay-warning-border: var(--btcpay-warning);
|
||||
--btcpay-warning-border-hover: var(--btcpay-warning-bg-hover);
|
||||
--btcpay-warning-border-active: var(--btcpay-warning-bg-active);
|
||||
--btcpay-warning-dim-bg: var(--btcpay-yellow-100);
|
||||
--btcpay-warning-dim-bg-striped: var(--btcpay-yellow-200);
|
||||
--btcpay-warning-dim-bg-hover: var(--btcpay-yellow-200);
|
||||
--btcpay-warning-dim-bg-active: var(--btcpay-yellow-300);
|
||||
--btcpay-warning-dim-border: var(--btcpay-warning-dim-bg);
|
||||
--btcpay-warning-dim-border-active: var(--btcpay-warning-dim-bg-active);
|
||||
--btcpay-warning-dim-text: var(--btcpay-neutral-800);
|
||||
--btcpay-warning-dim-text-striped: var(--btcpay-warning-dim-text);
|
||||
--btcpay-warning-dim-text-hover: var(--btcpay-warning-dim-text);
|
||||
--btcpay-warning-dim-text-active: var(--btcpay-yellow-900);
|
||||
--btcpay-warning-shadow: rgba(217, 164, 6, 0.33);
|
||||
--btcpay-warning-rgb: var(--btcpay-yellow-rgb);
|
||||
--btcpay-danger: var(--btcpay-red-500);
|
||||
--btcpay-danger-accent: var(--btcpay-red-700);
|
||||
--btcpay-danger-text: var(--btcpay-white);
|
||||
--btcpay-danger-text-hover: var(--btcpay-white);
|
||||
--btcpay-danger-text-active: var(--btcpay-white);
|
||||
--btcpay-danger-bg-hover: var(--btcpay-danger-accent);
|
||||
--btcpay-danger-bg-active: var(--btcpay-danger-accent);
|
||||
--btcpay-danger-border: var(--btcpay-danger);
|
||||
--btcpay-danger-border-hover: var(--btcpay-danger-bg-hover);
|
||||
--btcpay-danger-border-active: var(--btcpay-danger-bg-active);
|
||||
--btcpay-danger-dim-bg: var(--btcpay-red-100);
|
||||
--btcpay-danger-dim-bg-striped: var(--btcpay-red-200);
|
||||
--btcpay-danger-dim-bg-hover: var(--btcpay-red-200);
|
||||
--btcpay-danger-dim-bg-active: var(--btcpay-red-300);
|
||||
--btcpay-danger-dim-border: var(--btcpay-danger-dim-bg);
|
||||
--btcpay-danger-dim-border-active: var(--btcpay-danger-dim-bg-active);
|
||||
--btcpay-danger-dim-text: var(--btcpay-red-800);
|
||||
--btcpay-danger-dim-text-striped: var(--btcpay-danger-dim-text);
|
||||
--btcpay-danger-dim-text-hover: var(--btcpay-danger-dim-text);
|
||||
--btcpay-danger-dim-text-active: var(--btcpay-red-900);
|
||||
--btcpay-danger-shadow: rgba(225, 83, 97, 0.33);
|
||||
--btcpay-danger-rgb: var(--btcpay-red-rgb);
|
||||
--btcpay-light: var(--btcpay-neutral-200);
|
||||
--btcpay-light-accent: var(--btcpay-neutral-400);
|
||||
--btcpay-light-text: var(--btcpay-neutral-800);
|
||||
--btcpay-light-text-hover: var(--btcpay-neutral-800);
|
||||
--btcpay-light-text-active: var(--btcpay-neutral-800);
|
||||
--btcpay-light-bg-hover: var(--btcpay-light-accent);
|
||||
--btcpay-light-bg-active: var(--btcpay-light-accent);
|
||||
--btcpay-light-border: var(--btcpay-light);
|
||||
--btcpay-light-border-hover: var(--btcpay-light-bg-hover);
|
||||
--btcpay-light-border-active: var(--btcpay-light-bg-active);
|
||||
--btcpay-light-dim-bg: var(--btcpay-white);
|
||||
--btcpay-light-dim-bg-striped: var(--btcpay-neutral-200);
|
||||
--btcpay-light-dim-bg-hover: var(--btcpay-neutral-200);
|
||||
--btcpay-light-dim-bg-active: var(--btcpay-neutral-300);
|
||||
--btcpay-light-dim-border: var(--btcpay-light-dim-bg);
|
||||
--btcpay-light-dim-border-active: var(--btcpay-light-dim-bg-active);
|
||||
--btcpay-light-dim-text: var(--btcpay-neutral-800);
|
||||
--btcpay-light-dim-text-striped: var(--btcpay-light-dim-text);
|
||||
--btcpay-light-dim-text-hover: var(--btcpay-light-dim-text);
|
||||
--btcpay-light-dim-text-active: var(--btcpay-neutral-900);
|
||||
--btcpay-light-shadow: rgba(211, 212, 213, 0.33);
|
||||
--btcpay-light-rgb: 233, 236, 239;
|
||||
--btcpay-dark: var(--btcpay-neutral-800);
|
||||
--btcpay-dark-accent: var(--btcpay-black);
|
||||
--btcpay-dark-text: var(--btcpay-neutral-200);
|
||||
--btcpay-dark-text-hover: var(--btcpay-neutral-200);
|
||||
--btcpay-dark-text-active: var(--btcpay-neutral-200);
|
||||
--btcpay-dark-bg-hover: var(--btcpay-dark-accent);
|
||||
--btcpay-dark-bg-active: var(--btcpay-dark-accent);
|
||||
--btcpay-dark-border: var(--btcpay-dark);
|
||||
--btcpay-dark-border-hover: var(--btcpay-dark-bg-hover);
|
||||
--btcpay-dark-border-active: var(--btcpay-dark-bg-active);
|
||||
--btcpay-dark-dim-bg: var(--btcpay-neutral-900);
|
||||
--btcpay-dark-dim-bg-striped: var(--btcpay-neutral-900);
|
||||
--btcpay-dark-dim-bg-hover: var(--btcpay-neutral-800);
|
||||
--btcpay-dark-dim-bg-active: var(--btcpay-neutral-700);
|
||||
--btcpay-dark-dim-border: var(--btcpay-dark-dim-bg);
|
||||
--btcpay-dark-dim-border-active: var(--btcpay-dark-dim-bg-active);
|
||||
--btcpay-dark-dim-text: var(--btcpay-neutral-200);
|
||||
--btcpay-dark-dim-text-striped: var(--btcpay-dark-dim-text);
|
||||
--btcpay-dark-dim-text-hover: var(--btcpay-dark-dim-text);
|
||||
--btcpay-dark-dim-text-active: var(--btcpay-neutral-100);
|
||||
--btcpay-dark-shadow: rgba(66, 70, 73, 0.33);
|
||||
--btcpay-dark-rgb: 33, 38, 45;
|
||||
}
|
||||
|
||||
:root[data-theme="dark"],
|
||||
:root[data-btcpay-theme="dark"] {
|
||||
--btcpay-neutral-100: var(--btcpay-neutral-dark-100);
|
||||
--btcpay-neutral-200: var(--btcpay-neutral-dark-200);
|
||||
--btcpay-neutral-300: var(--btcpay-neutral-dark-300);
|
||||
--btcpay-neutral-400: var(--btcpay-neutral-dark-400);
|
||||
--btcpay-neutral-500: var(--btcpay-neutral-dark-500);
|
||||
--btcpay-neutral-600: var(--btcpay-neutral-dark-600);
|
||||
--btcpay-neutral-700: var(--btcpay-neutral-dark-700);
|
||||
--btcpay-neutral-800: var(--btcpay-neutral-dark-800);
|
||||
--btcpay-neutral-900: var(--btcpay-neutral-dark-900);
|
||||
--btcpay-neutral-950: #0D1117;
|
||||
--btcpay-bg-dark: var(--btcpay-neutral-950);
|
||||
--btcpay-bg-tile: var(--btcpay-bg-dark);
|
||||
--btcpay-body-bg: var(--btcpay-neutral-900);
|
||||
--btcpay-body-bg-light: var(--btcpay-neutral-950);
|
||||
--btcpay-body-bg-medium: var(--btcpay-neutral-800);
|
||||
--btcpay-body-bg-striped: var(--btcpay-neutral-800);
|
||||
--btcpay-body-bg-hover: var(--btcpay-neutral-950);
|
||||
--btcpay-body-bg-rgb: 41, 41, 41;
|
||||
--btcpay-body-border-light: var(--btcpay-neutral-800);
|
||||
--btcpay-body-border-medium: var(--btcpay-neutral-700);
|
||||
--btcpay-body-text: var(--btcpay-white);
|
||||
--btcpay-body-text-rgb: 255, 255, 255;
|
||||
--btcpay-body-link-accent: var(--btcpay-primary-300);
|
||||
--btcpay-form-bg: var(--btcpay-neutral-950);
|
||||
--btcpay-form-bg-addon: var(--btcpay-neutral-700);
|
||||
--btcpay-form-bg-disabled: var(--btcpay-neutral-800);
|
||||
--btcpay-form-text: var(--btcpay-neutral-200);
|
||||
--btcpay-form-text-label: var(--btcpay-neutral-100);
|
||||
--btcpay-form-text-addon: var(--btcpay-neutral-300);
|
||||
--btcpay-form-border: var(--btcpay-neutral-800);
|
||||
--btcpay-form-border-check: var(--btcpay-neutral-600);
|
||||
--btcpay-header-bg: var(--btcpay-bg-dark);
|
||||
--btcpay-nav-link: var(--btcpay-neutral-500);
|
||||
--btcpay-nav-link-accent: var(--btcpay-neutral-300);
|
||||
--btcpay-nav-link-active: var(--btcpay-white);
|
||||
--btcpay-footer-text: var(--btcpay-neutral-400);
|
||||
--btcpay-footer-link: var(--btcpay-neutral-400);
|
||||
--btcpay-footer-link-accent: var(--btcpay-neutral-200);
|
||||
--btcpay-pre-bg: var(--btcpay-bg-dark);
|
||||
--btcpay-secondary: transparent;
|
||||
--btcpay-secondary-text-active: var(--btcpay-primary);
|
||||
--btcpay-secondary-border: var(--btcpay-neutral-700);
|
||||
--btcpay-secondary-rgb: 22, 27, 34;
|
||||
--btcpay-light: var(--btcpay-neutral-800);
|
||||
--btcpay-light-accent: var(--btcpay-black);
|
||||
--btcpay-light-text: var(--btcpay-neutral-200);
|
||||
--btcpay-light-text-hover: var(--btcpay-neutral-200);
|
||||
--btcpay-light-text-active: var(--btcpay-neutral-200);
|
||||
--btcpay-light-bg-hover: var(--btcpay-light-accent);
|
||||
--btcpay-light-bg-active: var(--btcpay-light-accent);
|
||||
--btcpay-light-border: var(--btcpay-light);
|
||||
--btcpay-light-border-hover: var(--btcpay-light-bg-hover);
|
||||
--btcpay-light-border-active: var(--btcpay-light-bg-active);
|
||||
--btcpay-light-dim-bg: var(--btcpay-neutral-950);
|
||||
--btcpay-light-dim-bg-striped: var(--btcpay-neutral-900);
|
||||
--btcpay-light-dim-bg-hover: var(--btcpay-neutral-800);
|
||||
--btcpay-light-dim-bg-active: var(--btcpay-neutral-700);
|
||||
--btcpay-light-dim-border: var(--btcpay-light-dim-bg);
|
||||
--btcpay-light-dim-border-active: var(--btcpay-light-dim-bg-active);
|
||||
--btcpay-light-dim-text: var(--btcpay-neutral-200);
|
||||
--btcpay-light-dim-text-striped: var(--btcpay-light-dim-text);
|
||||
--btcpay-light-dim-text-hover: var(--btcpay-light-dim-text);
|
||||
--btcpay-light-dim-text-active: var(--btcpay-neutral-100);
|
||||
--btcpay-light-shadow: rgba(66, 70, 73, 0.33);
|
||||
--btcpay-light-rgb: 33, 38, 45;
|
||||
--btcpay-dark: var(--btcpay-neutral-200);
|
||||
--btcpay-dark-accent: var(--btcpay-neutral-400);
|
||||
--btcpay-dark-text: var(--btcpay-neutral-800);
|
||||
--btcpay-dark-text-hover: var(--btcpay-neutral-800);
|
||||
--btcpay-dark-text-active: var(--btcpay-neutral-800);
|
||||
--btcpay-dark-bg-hover: var(--btcpay-dark-accent);
|
||||
--btcpay-dark-bg-active: var(--btcpay-dark-accent);
|
||||
--btcpay-dark-border: var(--btcpay-dark);
|
||||
--btcpay-dark-border-hover: var(--btcpay-dark-bg-hover);
|
||||
--btcpay-dark-border-active: var(--btcpay-dark-bg-active);
|
||||
--btcpay-dark-dim-bg: var(--btcpay-white);
|
||||
--btcpay-dark-dim-bg-striped: var(--btcpay-neutral-200);
|
||||
--btcpay-dark-dim-bg-hover: var(--btcpay-neutral-200);
|
||||
--btcpay-dark-dim-bg-active: var(--btcpay-neutral-300);
|
||||
--btcpay-dark-dim-border: var(--btcpay-dark-dim-bg);
|
||||
--btcpay-dark-dim-border-active: var(--btcpay-dark-dim-bg-active);
|
||||
--btcpay-dark-dim-text: var(--btcpay-neutral-800);
|
||||
--btcpay-dark-dim-text-striped: var(--btcpay-dark-dim-text);
|
||||
--btcpay-dark-dim-text-hover: var(--btcpay-dark-dim-text);
|
||||
--btcpay-dark-dim-text-active: var(--btcpay-neutral-900);
|
||||
--btcpay-dark-shadow: rgba(211, 212, 213, 0.33);
|
||||
--btcpay-dark-rgb: 201, 209, 217;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root:not([data-btcpay-theme],[data-theme]) {
|
||||
--btcpay-neutral-100: var(--btcpay-neutral-dark-100);
|
||||
--btcpay-neutral-200: var(--btcpay-neutral-dark-200);
|
||||
--btcpay-neutral-300: var(--btcpay-neutral-dark-300);
|
||||
--btcpay-neutral-400: var(--btcpay-neutral-dark-400);
|
||||
--btcpay-neutral-500: var(--btcpay-neutral-dark-500);
|
||||
--btcpay-neutral-600: var(--btcpay-neutral-dark-600);
|
||||
--btcpay-neutral-700: var(--btcpay-neutral-dark-700);
|
||||
--btcpay-neutral-800: var(--btcpay-neutral-dark-800);
|
||||
--btcpay-neutral-900: var(--btcpay-neutral-dark-900);
|
||||
--btcpay-neutral-950: #0D1117;
|
||||
--btcpay-bg-dark: var(--btcpay-neutral-950);
|
||||
--btcpay-bg-tile: var(--btcpay-bg-dark);
|
||||
--btcpay-body-bg: var(--btcpay-neutral-900);
|
||||
--btcpay-body-bg-light: var(--btcpay-neutral-950);
|
||||
--btcpay-body-bg-medium: var(--btcpay-neutral-800);
|
||||
--btcpay-body-bg-striped: var(--btcpay-neutral-800);
|
||||
--btcpay-body-bg-hover: var(--btcpay-neutral-950);
|
||||
--btcpay-body-bg-rgb: 41, 41, 41;
|
||||
--btcpay-body-border-light: var(--btcpay-neutral-800);
|
||||
--btcpay-body-border-medium: var(--btcpay-neutral-700);
|
||||
--btcpay-body-text: var(--btcpay-white);
|
||||
--btcpay-body-text-rgb: 255, 255, 255;
|
||||
--btcpay-body-link-accent: var(--btcpay-primary-300);
|
||||
--btcpay-form-bg: var(--btcpay-neutral-950);
|
||||
--btcpay-form-bg-addon: var(--btcpay-neutral-700);
|
||||
--btcpay-form-bg-disabled: var(--btcpay-neutral-800);
|
||||
--btcpay-form-text: var(--btcpay-neutral-200);
|
||||
--btcpay-form-text-label: var(--btcpay-neutral-100);
|
||||
--btcpay-form-text-addon: var(--btcpay-neutral-300);
|
||||
--btcpay-form-border: var(--btcpay-neutral-800);
|
||||
--btcpay-form-border-check: var(--btcpay-neutral-600);
|
||||
--btcpay-header-bg: var(--btcpay-bg-dark);
|
||||
--btcpay-nav-link: var(--btcpay-neutral-500);
|
||||
--btcpay-nav-link-accent: var(--btcpay-neutral-300);
|
||||
--btcpay-nav-link-active: var(--btcpay-white);
|
||||
--btcpay-footer-text: var(--btcpay-neutral-400);
|
||||
--btcpay-footer-link: var(--btcpay-neutral-400);
|
||||
--btcpay-footer-link-accent: var(--btcpay-neutral-200);
|
||||
--btcpay-pre-bg: var(--btcpay-bg-dark);
|
||||
--btcpay-secondary: transparent;
|
||||
--btcpay-secondary-text-active: var(--btcpay-primary);
|
||||
--btcpay-secondary-border: var(--btcpay-neutral-700);
|
||||
--btcpay-secondary-rgb: 22, 27, 34;
|
||||
--btcpay-light: var(--btcpay-neutral-800);
|
||||
--btcpay-light-accent: var(--btcpay-black);
|
||||
--btcpay-light-text: var(--btcpay-neutral-200);
|
||||
--btcpay-light-text-hover: var(--btcpay-neutral-200);
|
||||
--btcpay-light-text-active: var(--btcpay-neutral-200);
|
||||
--btcpay-light-bg-hover: var(--btcpay-light-accent);
|
||||
--btcpay-light-bg-active: var(--btcpay-light-accent);
|
||||
--btcpay-light-border: var(--btcpay-light);
|
||||
--btcpay-light-border-hover: var(--btcpay-light-bg-hover);
|
||||
--btcpay-light-border-active: var(--btcpay-light-bg-active);
|
||||
--btcpay-light-dim-bg: var(--btcpay-neutral-950);
|
||||
--btcpay-light-dim-bg-striped: var(--btcpay-neutral-900);
|
||||
--btcpay-light-dim-bg-hover: var(--btcpay-neutral-800);
|
||||
--btcpay-light-dim-bg-active: var(--btcpay-neutral-700);
|
||||
--btcpay-light-dim-border: var(--btcpay-light-dim-bg);
|
||||
--btcpay-light-dim-border-active: var(--btcpay-light-dim-bg-active);
|
||||
--btcpay-light-dim-text: var(--btcpay-neutral-200);
|
||||
--btcpay-light-dim-text-striped: var(--btcpay-light-dim-text);
|
||||
--btcpay-light-dim-text-hover: var(--btcpay-light-dim-text);
|
||||
--btcpay-light-dim-text-active: var(--btcpay-neutral-100);
|
||||
--btcpay-light-shadow: rgba(66, 70, 73, 0.33);
|
||||
--btcpay-light-rgb: 33, 38, 45;
|
||||
--btcpay-dark: var(--btcpay-neutral-200);
|
||||
--btcpay-dark-accent: var(--btcpay-neutral-400);
|
||||
--btcpay-dark-text: var(--btcpay-neutral-800);
|
||||
--btcpay-dark-text-hover: var(--btcpay-neutral-800);
|
||||
--btcpay-dark-text-active: var(--btcpay-neutral-800);
|
||||
--btcpay-dark-bg-hover: var(--btcpay-dark-accent);
|
||||
--btcpay-dark-bg-active: var(--btcpay-dark-accent);
|
||||
--btcpay-dark-border: var(--btcpay-dark);
|
||||
--btcpay-dark-border-hover: var(--btcpay-dark-bg-hover);
|
||||
--btcpay-dark-border-active: var(--btcpay-dark-bg-active);
|
||||
--btcpay-dark-dim-bg: var(--btcpay-white);
|
||||
--btcpay-dark-dim-bg-striped: var(--btcpay-neutral-200);
|
||||
--btcpay-dark-dim-bg-hover: var(--btcpay-neutral-200);
|
||||
--btcpay-dark-dim-bg-active: var(--btcpay-neutral-300);
|
||||
--btcpay-dark-dim-border: var(--btcpay-dark-dim-bg);
|
||||
--btcpay-dark-dim-border-active: var(--btcpay-dark-dim-bg-active);
|
||||
--btcpay-dark-dim-text: var(--btcpay-neutral-800);
|
||||
--btcpay-dark-dim-text-striped: var(--btcpay-dark-dim-text);
|
||||
--btcpay-dark-dim-text-hover: var(--btcpay-dark-dim-text);
|
||||
--btcpay-dark-dim-text-active: var(--btcpay-neutral-900);
|
||||
--btcpay-dark-shadow: rgba(211, 212, 213, 0.33);
|
||||
--btcpay-dark-rgb: 201, 209, 217;
|
||||
}
|
||||
}
|
||||
10
PluginBuilder/wwwroot/svg/logo.svg
Normal file
10
PluginBuilder/wwwroot/svg/logo.svg
Normal file
@ -0,0 +1,10 @@
|
||||
<svg class="logo" viewBox="0 0 192 84" xmlns="http://www.w3.org/2000/svg">
|
||||
<g>
|
||||
<path d="M5.206 83.433a4.86 4.86 0 01-4.859-4.861V5.431a4.86 4.86 0 119.719 0v73.141a4.861 4.861 0 01-4.86 4.861" fill="#CEDC21" class="logo-brand-light"/>
|
||||
<path d="M5.209 83.433a4.862 4.862 0 01-2.086-9.253L32.43 60.274 2.323 38.093a4.861 4.861 0 015.766-7.826l36.647 26.999a4.864 4.864 0 01-.799 8.306L7.289 82.964a4.866 4.866 0 01-2.08.469" fill="#51B13E" class="logo-brand-medium"/>
|
||||
<path d="M5.211 54.684a4.86 4.86 0 01-2.887-8.774L32.43 23.73 3.123 9.821a4.861 4.861 0 014.166-8.784l36.648 17.394a4.86 4.86 0 01.799 8.305l-36.647 27a4.844 4.844 0 01-2.878.948" fill="#CEDC21" class="logo-brand-light"/>
|
||||
<path d="M10.066 31.725v20.553L24.01 42.006z" fill="#1E7A44" class="logo-brand-dark"/>
|
||||
<path d="M10.066 5.431A4.861 4.861 0 005.206.57 4.86 4.86 0 00.347 5.431v61.165h9.72V5.431h-.001z" fill="#CEDC21" class="logo-brand-light"/>
|
||||
<path d="M74.355 41.412c3.114.884 4.84 3.704 4.84 7.238 0 5.513-3.368 8.082-7.955 8.082H60.761V27.271h9.259c4.504 0 7.997 2.146 7.997 7.743 0 2.821-1.179 5.43-3.662 6.398m-4.293-.716c3.324 0 6.018-1.179 6.018-5.724 0-4.586-2.776-5.808-6.145-5.808h-7.197v11.531h7.324v.001zm1.052 14.099c3.366 0 6.06-1.768 6.06-6.145 0-4.713-3.072-6.144-6.901-6.144h-7.534v12.288h8.375v.001zM98.893 27.271v1.81h-8.122v27.651h-1.979V29.081h-8.123v-1.81zM112.738 26.85c5.01 0 9.554 2.524 10.987 8.543h-1.895c-1.348-4.923-5.303-6.732-9.134-6.732-6.944 0-10.605 5.681-10.605 13.341 0 8.08 3.661 13.256 10.646 13.256 4.125 0 7.828-1.85 9.26-7.279h1.895c-1.264 6.271-6.229 9.174-11.154 9.174-7.87 0-12.583-5.808-12.583-15.15 0-8.966 4.969-15.153 12.583-15.153M138.709 27.271c5.091 0 8.795 3.326 8.795 9.764 0 6.06-3.704 9.722-8.795 9.722h-7.746v9.976h-1.935V27.271h9.681zm0 17.549c3.745 0 6.816-2.397 6.816-7.827 0-5.429-2.947-7.869-6.816-7.869h-7.746V44.82h7.746zM147.841 56.732v-.255l11.741-29.29h.885l11.615 29.29v.255h-2.062l-3.322-8.501H153.27l-3.324 8.501h-2.105zm12.164-26.052l-6.059 15.697h12.078l-6.019-15.697zM189.551 27.271h2.104v.293l-9.176 16.92v12.248h-2.02V44.484l-9.216-16.961v-.252h2.147l3.997 7.492 4.043 7.786h.04l4.081-7.786z" class="logo-brand-text"/>
|
||||
</g>
|
||||
</svg>
|
||||
13
README.md
Normal file
13
README.md
Normal file
@ -0,0 +1,13 @@
|
||||
# Introduction
|
||||
|
||||
This project hosts a server with a front end which can be used to build BTCPay Server plugins and store the binaries on some storage.
|
||||
|
||||
## Prerequisite:
|
||||
|
||||
It assumes you installed docker on your system.
|
||||
|
||||
## Configuration
|
||||
|
||||
All parameters are configured via environment variables.
|
||||
|
||||
* `PB_POSTGRES`: Connection to a postgres database (example: `User ID=postgres;Include Error Detail=true;Host=127.0.0.1;Port=61932;Database=blah`)
|
||||
34
btcpayserver-plugin-builder.sln
Normal file
34
btcpayserver-plugin-builder.sln
Normal file
@ -0,0 +1,34 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.30114.105
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PluginBuilder.Tests", "PluginBuilder.Tests\PluginBuilder.Tests.csproj", "{973D71D2-B713-4A95-A98C-D714FFB98AC6}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PluginBuilder", "PluginBuilder\PluginBuilder.csproj", "{A1928D9E-3939-42A7-BC39-01D7F2746C7B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PluginBuilder.Targets", "PluginBuilder.Targets\PluginBuilder.Targets.csproj", "{D76DBE30-BEA4-4AF0-9CF4-D0781E127D2A}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{973D71D2-B713-4A95-A98C-D714FFB98AC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{973D71D2-B713-4A95-A98C-D714FFB98AC6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{973D71D2-B713-4A95-A98C-D714FFB98AC6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{973D71D2-B713-4A95-A98C-D714FFB98AC6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A1928D9E-3939-42A7-BC39-01D7F2746C7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A1928D9E-3939-42A7-BC39-01D7F2746C7B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A1928D9E-3939-42A7-BC39-01D7F2746C7B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A1928D9E-3939-42A7-BC39-01D7F2746C7B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D76DBE30-BEA4-4AF0-9CF4-D0781E127D2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D76DBE30-BEA4-4AF0-9CF4-D0781E127D2A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D76DBE30-BEA4-4AF0-9CF4-D0781E127D2A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D76DBE30-BEA4-4AF0-9CF4-D0781E127D2A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
Loading…
Reference in New Issue
Block a user