Compare commits
5 Commits
mononaut/a
...
knorrium/u
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
541555051b | ||
|
|
5f5e96984a | ||
|
|
f5a54ae62b | ||
|
|
4da145ff5c | ||
|
|
a898701830 |
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
@@ -9,7 +9,7 @@ jobs:
|
|||||||
if: "!contains(github.event.pull_request.labels.*.name, 'ops') && !contains(github.head_ref, 'ops/')"
|
if: "!contains(github.event.pull_request.labels.*.name, 'ops') && !contains(github.head_ref, 'ops/')"
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node: ["20", "21"]
|
node: ["20", "22"]
|
||||||
flavor: ["dev", "prod"]
|
flavor: ["dev", "prod"]
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
runs-on: "ubuntu-latest"
|
runs-on: "ubuntu-latest"
|
||||||
@@ -160,7 +160,7 @@ jobs:
|
|||||||
if: "!contains(github.event.pull_request.labels.*.name, 'ops') && !contains(github.head_ref, 'ops/')"
|
if: "!contains(github.event.pull_request.labels.*.name, 'ops') && !contains(github.head_ref, 'ops/')"
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node: ["20", "21"]
|
node: ["20", "22"]
|
||||||
flavor: ["dev", "prod"]
|
flavor: ["dev", "prod"]
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
runs-on: "ubuntu-latest"
|
runs-on: "ubuntu-latest"
|
||||||
@@ -263,7 +263,7 @@ jobs:
|
|||||||
- name: Setup node
|
- name: Setup node
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 22
|
||||||
cache: "npm"
|
cache: "npm"
|
||||||
cache-dependency-path: ${{ matrix.module }}/frontend/package-lock.json
|
cache-dependency-path: ${{ matrix.module }}/frontend/package-lock.json
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const config: Config.InitialOptions = {
|
|||||||
automock: false,
|
automock: false,
|
||||||
collectCoverage: true,
|
collectCoverage: true,
|
||||||
collectCoverageFrom: ["./src/**/**.ts"],
|
collectCoverageFrom: ["./src/**/**.ts"],
|
||||||
coverageProvider: "v8",
|
coverageProvider: "babel",
|
||||||
coverageThreshold: {
|
coverageThreshold: {
|
||||||
global: {
|
global: {
|
||||||
lines: 1
|
lines: 1
|
||||||
|
|||||||
55
backend/package-lock.json
generated
55
backend/package-lock.json
generated
@@ -10,6 +10,7 @@
|
|||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "GNU Affero General Public License v3.0",
|
"license": "GNU Affero General Public License v3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@babel/core": "^7.25.2",
|
||||||
"@mempool/electrum-client": "1.1.9",
|
"@mempool/electrum-client": "1.1.9",
|
||||||
"@types/node": "^18.15.3",
|
"@types/node": "^18.15.3",
|
||||||
"axios": "1.7.2",
|
"axios": "1.7.2",
|
||||||
@@ -17,7 +18,7 @@
|
|||||||
"crypto-js": "~4.2.0",
|
"crypto-js": "~4.2.0",
|
||||||
"express": "~4.21.1",
|
"express": "~4.21.1",
|
||||||
"maxmind": "~4.3.11",
|
"maxmind": "~4.3.11",
|
||||||
"mysql2": "~3.12.0",
|
"mysql2": "~3.11.0",
|
||||||
"redis": "^4.7.0",
|
"redis": "^4.7.0",
|
||||||
"rust-gbt": "file:./rust-gbt",
|
"rust-gbt": "file:./rust-gbt",
|
||||||
"socks-proxy-agent": "~7.0.0",
|
"socks-proxy-agent": "~7.0.0",
|
||||||
@@ -25,6 +26,8 @@
|
|||||||
"ws": "~8.18.0"
|
"ws": "~8.18.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@babel/code-frame": "^7.18.6",
|
||||||
|
"@babel/core": "^7.25.2",
|
||||||
"@types/compression": "^1.7.2",
|
"@types/compression": "^1.7.2",
|
||||||
"@types/crypto-js": "^4.1.1",
|
"@types/crypto-js": "^4.1.1",
|
||||||
"@types/express": "^4.17.17",
|
"@types/express": "^4.17.17",
|
||||||
@@ -5997,21 +6000,6 @@
|
|||||||
"yallist": "^3.0.2"
|
"yallist": "^3.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lru.min": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-FbAj6lXil6t8z4z3j0E5mfRlPzxkySotzUHwRXjlpRh10vc6AI6WN62ehZj82VG7M20rqogJ0GLwar2Xa05a8Q==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"bun": ">=1.0.0",
|
|
||||||
"deno": ">=1.30.0",
|
|
||||||
"node": ">=8.0.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/wellwelwel"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/make-dir": {
|
"node_modules/make-dir": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
|
||||||
@@ -6173,17 +6161,16 @@
|
|||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
},
|
},
|
||||||
"node_modules/mysql2": {
|
"node_modules/mysql2": {
|
||||||
"version": "3.12.0",
|
"version": "3.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.11.0.tgz",
|
||||||
"integrity": "sha512-C8fWhVysZoH63tJbX8d10IAoYCyXy4fdRFz2Ihrt9jtPILYynFEKUUzpp1U7qxzDc3tMbotvaBH+sl6bFnGZiw==",
|
"integrity": "sha512-J9phbsXGvTOcRVPR95YedzVSxJecpW5A5+cQ57rhHIFXteTP10HCs+VBjS7DHIKfEaI1zQ5tlVrquCd64A6YvA==",
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"aws-ssl-profiles": "^1.1.1",
|
"aws-ssl-profiles": "^1.1.1",
|
||||||
"denque": "^2.1.0",
|
"denque": "^2.1.0",
|
||||||
"generate-function": "^2.3.1",
|
"generate-function": "^2.3.1",
|
||||||
"iconv-lite": "^0.6.3",
|
"iconv-lite": "^0.6.3",
|
||||||
"long": "^5.2.1",
|
"long": "^5.2.1",
|
||||||
"lru.min": "^1.0.0",
|
"lru-cache": "^8.0.0",
|
||||||
"named-placeholders": "^1.1.3",
|
"named-placeholders": "^1.1.3",
|
||||||
"seq-queue": "^0.0.5",
|
"seq-queue": "^0.0.5",
|
||||||
"sqlstring": "^2.3.2"
|
"sqlstring": "^2.3.2"
|
||||||
@@ -6203,6 +6190,14 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/mysql2/node_modules/lru-cache": {
|
||||||
|
"version": "8.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-8.0.5.tgz",
|
||||||
|
"integrity": "sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.14"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/named-placeholders": {
|
"node_modules/named-placeholders": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz",
|
||||||
@@ -12218,11 +12213,6 @@
|
|||||||
"yallist": "^3.0.2"
|
"yallist": "^3.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lru.min": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-FbAj6lXil6t8z4z3j0E5mfRlPzxkySotzUHwRXjlpRh10vc6AI6WN62ehZj82VG7M20rqogJ0GLwar2Xa05a8Q=="
|
|
||||||
},
|
|
||||||
"make-dir": {
|
"make-dir": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
|
||||||
@@ -12337,16 +12327,16 @@
|
|||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
},
|
},
|
||||||
"mysql2": {
|
"mysql2": {
|
||||||
"version": "3.12.0",
|
"version": "3.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.11.0.tgz",
|
||||||
"integrity": "sha512-C8fWhVysZoH63tJbX8d10IAoYCyXy4fdRFz2Ihrt9jtPILYynFEKUUzpp1U7qxzDc3tMbotvaBH+sl6bFnGZiw==",
|
"integrity": "sha512-J9phbsXGvTOcRVPR95YedzVSxJecpW5A5+cQ57rhHIFXteTP10HCs+VBjS7DHIKfEaI1zQ5tlVrquCd64A6YvA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"aws-ssl-profiles": "^1.1.1",
|
"aws-ssl-profiles": "^1.1.1",
|
||||||
"denque": "^2.1.0",
|
"denque": "^2.1.0",
|
||||||
"generate-function": "^2.3.1",
|
"generate-function": "^2.3.1",
|
||||||
"iconv-lite": "^0.6.3",
|
"iconv-lite": "^0.6.3",
|
||||||
"long": "^5.2.1",
|
"long": "^5.2.1",
|
||||||
"lru.min": "^1.0.0",
|
"lru-cache": "^8.0.0",
|
||||||
"named-placeholders": "^1.1.3",
|
"named-placeholders": "^1.1.3",
|
||||||
"seq-queue": "^0.0.5",
|
"seq-queue": "^0.0.5",
|
||||||
"sqlstring": "^2.3.2"
|
"sqlstring": "^2.3.2"
|
||||||
@@ -12359,6 +12349,11 @@
|
|||||||
"requires": {
|
"requires": {
|
||||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"lru-cache": {
|
||||||
|
"version": "8.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-8.0.5.tgz",
|
||||||
|
"integrity": "sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -39,6 +39,7 @@
|
|||||||
"prettier": "./node_modules/.bin/prettier --write \"src/**/*.{js,ts}\""
|
"prettier": "./node_modules/.bin/prettier --write \"src/**/*.{js,ts}\""
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@babel/core": "^7.25.2",
|
||||||
"@mempool/electrum-client": "1.1.9",
|
"@mempool/electrum-client": "1.1.9",
|
||||||
"@types/node": "^18.15.3",
|
"@types/node": "^18.15.3",
|
||||||
"axios": "1.7.2",
|
"axios": "1.7.2",
|
||||||
@@ -46,7 +47,7 @@
|
|||||||
"crypto-js": "~4.2.0",
|
"crypto-js": "~4.2.0",
|
||||||
"express": "~4.21.1",
|
"express": "~4.21.1",
|
||||||
"maxmind": "~4.3.11",
|
"maxmind": "~4.3.11",
|
||||||
"mysql2": "~3.12.0",
|
"mysql2": "~3.11.0",
|
||||||
"rust-gbt": "file:./rust-gbt",
|
"rust-gbt": "file:./rust-gbt",
|
||||||
"redis": "^4.7.0",
|
"redis": "^4.7.0",
|
||||||
"socks-proxy-agent": "~7.0.0",
|
"socks-proxy-agent": "~7.0.0",
|
||||||
@@ -54,6 +55,8 @@
|
|||||||
"ws": "~8.18.0"
|
"ws": "~8.18.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@babel/code-frame": "^7.18.6",
|
||||||
|
"@babel/core": "^7.25.2",
|
||||||
"@types/compression": "^1.7.2",
|
"@types/compression": "^1.7.2",
|
||||||
"@types/crypto-js": "^4.1.1",
|
"@types/crypto-js": "^4.1.1",
|
||||||
"@types/express": "^4.17.17",
|
"@types/express": "^4.17.17",
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ __SERVICES_API__=${SERVICES_API:=https://mempool.space/api/v1/services}
|
|||||||
__PUBLIC_ACCELERATIONS__=${PUBLIC_ACCELERATIONS:=false}
|
__PUBLIC_ACCELERATIONS__=${PUBLIC_ACCELERATIONS:=false}
|
||||||
__HISTORICAL_PRICE__=${HISTORICAL_PRICE:=true}
|
__HISTORICAL_PRICE__=${HISTORICAL_PRICE:=true}
|
||||||
__ADDITIONAL_CURRENCIES__=${ADDITIONAL_CURRENCIES:=false}
|
__ADDITIONAL_CURRENCIES__=${ADDITIONAL_CURRENCIES:=false}
|
||||||
__STRATUM_ENABLED__=${STRATUM_ENABLED:=false}
|
|
||||||
|
|
||||||
# Export as environment variables to be used by envsubst
|
# Export as environment variables to be used by envsubst
|
||||||
export __MAINNET_ENABLED__
|
export __MAINNET_ENABLED__
|
||||||
@@ -77,7 +76,6 @@ export __SERVICES_API__
|
|||||||
export __PUBLIC_ACCELERATIONS__
|
export __PUBLIC_ACCELERATIONS__
|
||||||
export __HISTORICAL_PRICE__
|
export __HISTORICAL_PRICE__
|
||||||
export __ADDITIONAL_CURRENCIES__
|
export __ADDITIONAL_CURRENCIES__
|
||||||
export __STRATUM_ENABLED__
|
|
||||||
|
|
||||||
folder=$(find /var/www/mempool -name "config.js" | xargs dirname)
|
folder=$(find /var/www/mempool -name "config.js" | xargs dirname)
|
||||||
echo ${folder}
|
echo ${folder}
|
||||||
|
|||||||
30
frontend/package-lock.json
generated
30
frontend/package-lock.json
generated
@@ -33,7 +33,7 @@
|
|||||||
"browserify": "^17.0.0",
|
"browserify": "^17.0.0",
|
||||||
"clipboard": "^2.0.11",
|
"clipboard": "^2.0.11",
|
||||||
"domino": "^2.1.6",
|
"domino": "^2.1.6",
|
||||||
"echarts": "~5.6.0",
|
"echarts": "~5.5.0",
|
||||||
"esbuild": "^0.24.0",
|
"esbuild": "^0.24.0",
|
||||||
"ngx-echarts": "~17.2.0",
|
"ngx-echarts": "~17.2.0",
|
||||||
"ngx-infinite-scroll": "^17.0.0",
|
"ngx-infinite-scroll": "^17.0.0",
|
||||||
@@ -8724,12 +8724,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/echarts": {
|
"node_modules/echarts": {
|
||||||
"version": "5.6.0",
|
"version": "5.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/echarts/-/echarts-5.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/echarts/-/echarts-5.5.0.tgz",
|
||||||
"integrity": "sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==",
|
"integrity": "sha512-rNYnNCzqDAPCr4m/fqyUFv7fD9qIsd50S6GDFgO1DxZhncCsNsG7IfUlAlvZe5oSEQxtsjnHiUuppzccry93Xw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "2.3.0",
|
"tslib": "2.3.0",
|
||||||
"zrender": "5.6.1"
|
"zrender": "5.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/echarts/node_modules/tslib": {
|
"node_modules/echarts/node_modules/tslib": {
|
||||||
@@ -18366,9 +18366,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/zrender": {
|
"node_modules/zrender": {
|
||||||
"version": "5.6.1",
|
"version": "5.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/zrender/-/zrender-5.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/zrender/-/zrender-5.5.0.tgz",
|
||||||
"integrity": "sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==",
|
"integrity": "sha512-O3MilSi/9mwoovx77m6ROZM7sXShR/O/JIanvzTwjN3FORfLSr81PsUGd7jlaYOeds9d8tw82oP44+3YucVo+w==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "2.3.0"
|
"tslib": "2.3.0"
|
||||||
}
|
}
|
||||||
@@ -24485,12 +24485,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"echarts": {
|
"echarts": {
|
||||||
"version": "5.6.0",
|
"version": "5.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/echarts/-/echarts-5.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/echarts/-/echarts-5.5.0.tgz",
|
||||||
"integrity": "sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==",
|
"integrity": "sha512-rNYnNCzqDAPCr4m/fqyUFv7fD9qIsd50S6GDFgO1DxZhncCsNsG7IfUlAlvZe5oSEQxtsjnHiUuppzccry93Xw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"tslib": "2.3.0",
|
"tslib": "2.3.0",
|
||||||
"zrender": "5.6.1"
|
"zrender": "5.5.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": {
|
"tslib": {
|
||||||
@@ -31485,9 +31485,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"zrender": {
|
"zrender": {
|
||||||
"version": "5.6.1",
|
"version": "5.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/zrender/-/zrender-5.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/zrender/-/zrender-5.5.0.tgz",
|
||||||
"integrity": "sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==",
|
"integrity": "sha512-O3MilSi/9mwoovx77m6ROZM7sXShR/O/JIanvzTwjN3FORfLSr81PsUGd7jlaYOeds9d8tw82oP44+3YucVo+w==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"tslib": "2.3.0"
|
"tslib": "2.3.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -86,7 +86,7 @@
|
|||||||
"browserify": "^17.0.0",
|
"browserify": "^17.0.0",
|
||||||
"clipboard": "^2.0.11",
|
"clipboard": "^2.0.11",
|
||||||
"domino": "^2.1.6",
|
"domino": "^2.1.6",
|
||||||
"echarts": "~5.6.0",
|
"echarts": "~5.5.0",
|
||||||
"ngx-echarts": "~17.2.0",
|
"ngx-echarts": "~17.2.0",
|
||||||
"ngx-infinite-scroll": "^17.0.0",
|
"ngx-infinite-scroll": "^17.0.0",
|
||||||
"qrcode": "1.5.1",
|
"qrcode": "1.5.1",
|
||||||
|
|||||||
@@ -397,13 +397,13 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@if (canPayWithCashapp || canPayWithApplePay || canPayWithGooglePay || canPayWithCardOnFile) {
|
@if (canPayWithCashapp || canPayWithApplePay || canPayWithGooglePay) {
|
||||||
<div class="col-sm text-center flex-grow-0 d-flex flex-column justify-content-center align-items-center">
|
<div class="col-sm text-center flex-grow-0 d-flex flex-column justify-content-center align-items-center">
|
||||||
<p class="text-nowrap">—<span i18n="or">OR</span>—</p>
|
<p class="text-nowrap">—<span i18n="or">OR</span>—</p>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@if (canPayWithCashapp || canPayWithApplePay || canPayWithGooglePay || canPayWithCardOnFile) {
|
@if (canPayWithCashapp || canPayWithApplePay || canPayWithGooglePay) {
|
||||||
<div class="col-sm text-center d-flex flex-column justify-content-center align-items-center">
|
<div class="col-sm text-center d-flex flex-column justify-content-center align-items-center">
|
||||||
<p><ng-container i18n="transaction.pay|Pay button label">Pay</ng-container> <app-fiat [value]="cost"></app-fiat> with</p>
|
<p><ng-container i18n="transaction.pay|Pay button label">Pay</ng-container> <app-fiat [value]="cost"></app-fiat> with</p>
|
||||||
@if (canPayWithCashapp) {
|
@if (canPayWithCashapp) {
|
||||||
@@ -421,13 +421,6 @@
|
|||||||
<img src="/resources/google-pay.png" height=37>
|
<img src="/resources/google-pay.png" height=37>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@if (canPayWithCardOnFile) {
|
|
||||||
@if (canPayWithCashapp || canPayWithApplePay || canPayWithGooglePay) { <span class="mt-1 mb-1"></span> }
|
|
||||||
<div class="paymentMethod mx-2 d-flex justify-content-center align-items-center" style="width: 200px; height: 55px" (click)="moveToStep('cardonfile')">
|
|
||||||
<fa-icon style="font-size: 24px; color: white" [icon]="['fas', 'credit-card']"></fa-icon>
|
|
||||||
<span class="ml-2" style="font-size: 22px">{{ estimate?.availablePaymentMethods?.cardOnFile?.card?.brand }} {{ estimate?.availablePaymentMethods?.cardOnFile?.card?.last_4 }}</span>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@@ -450,7 +443,7 @@
|
|||||||
<button type="button" class="mt-1 btn btn-secondary btn-sm rounded-pill align-self-center" style="width: 200px" (click)="moveToStep('summary')" i18n="go-back">Go back</button>
|
<button type="button" class="mt-1 btn btn-secondary btn-sm rounded-pill align-self-center" style="width: 200px" (click)="moveToStep('summary')" i18n="go-back">Go back</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
} @else if (step === 'cashapp' || step === 'applepay' || step === 'googlepay' || step === 'cardonfile') {
|
} @else if (step === 'cashapp' || step === 'applepay' || step === 'googlepay') {
|
||||||
<!-- Show checkout page -->
|
<!-- Show checkout page -->
|
||||||
<div class="row mb-md-1 text-center" id="confirm-title">
|
<div class="row mb-md-1 text-center" id="confirm-title">
|
||||||
<div class="col-sm" id="confirm-payment-title">
|
<div class="col-sm" id="confirm-payment-title">
|
||||||
@@ -466,7 +459,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if (step === 'cashapp' && !loadingCashapp || step === 'applepay' && !loadingApplePay || step === 'googlepay' && !loadingGooglePay || step === 'cardonfile' && !loadingCardOnFile) {
|
@if (step === 'cashapp' && !loadingCashapp || step === 'applepay' && !loadingApplePay || step === 'googlepay' && !loadingGooglePay) {
|
||||||
<div class="row text-center mt-1">
|
<div class="row text-center mt-1">
|
||||||
<div class="col-sm">
|
<div class="col-sm">
|
||||||
<div class="form-group w-100">
|
<div class="form-group w-100">
|
||||||
@@ -491,13 +484,8 @@
|
|||||||
<div id="cash-app-pay" class="d-inline-block" style="height: 50px" [style]="loadingCashapp ? 'opacity: 0; width: 0px; height: 0px; pointer-events: none;' : ''"></div>
|
<div id="cash-app-pay" class="d-inline-block" style="height: 50px" [style]="loadingCashapp ? 'opacity: 0; width: 0px; height: 0px; pointer-events: none;' : ''"></div>
|
||||||
} @else if (step === 'googlepay') {
|
} @else if (step === 'googlepay') {
|
||||||
<div id="google-pay-button" class="d-inline-block" style="height: 50px" [style]="loadingGooglePay ? 'opacity: 0; width: 0px; height: 0px; pointer-events: none;' : ''"></div>
|
<div id="google-pay-button" class="d-inline-block" style="height: 50px" [style]="loadingGooglePay ? 'opacity: 0; width: 0px; height: 0px; pointer-events: none;' : ''"></div>
|
||||||
} @else if (step === 'cardonfile') {
|
|
||||||
<div class="paymentMethod mx-2 d-flex justify-content-center align-items-center ml-auto mr-auto" style="width: 200px; height: 55px" (click)="requestCardOnFilePayment()" [style]="loadingCardOnFile ? 'opacity: 0; width: 0px; height: 0px; pointer-events: none;' : ''">
|
|
||||||
<fa-icon style="font-size: 24px; color: white" [icon]="['fas', 'credit-card']"></fa-icon>
|
|
||||||
<span class="ml-2" style="font-size: 22px">{{ estimate?.availablePaymentMethods?.cardOnFile?.card?.brand }} {{ estimate?.availablePaymentMethods?.cardOnFile?.card?.last_4 }}</span>
|
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
@if (loadingCashapp || loadingApplePay || loadingGooglePay || loadingCardOnFile) {
|
@if (loadingCashapp || loadingApplePay || loadingGooglePay) {
|
||||||
<div display="d-flex flex-row justify-content-center">
|
<div display="d-flex flex-row justify-content-center">
|
||||||
<span i18n="accelerator.loading-payment-method">Loading payment method...</span>
|
<span i18n="accelerator.loading-payment-method">Loading payment method...</span>
|
||||||
<div class="ml-2 spinner-border text-light" style="width: 25px; height: 25px"></div>
|
<div class="ml-2 spinner-border text-light" style="width: 25px; height: 25px"></div>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { EnterpriseService } from '@app/services/enterprise.service';
|
|||||||
import { ApiService } from '@app/services/api.service';
|
import { ApiService } from '@app/services/api.service';
|
||||||
import { isDevMode } from '@angular/core';
|
import { isDevMode } from '@angular/core';
|
||||||
|
|
||||||
export type PaymentMethod = 'balance' | 'bitcoin' | 'cashapp' | 'applePay' | 'googlePay' | 'cardOnFile';
|
export type PaymentMethod = 'balance' | 'bitcoin' | 'cashapp' | 'applePay' | 'googlePay';
|
||||||
|
|
||||||
export type AccelerationEstimate = {
|
export type AccelerationEstimate = {
|
||||||
hasAccess: boolean;
|
hasAccess: boolean;
|
||||||
@@ -26,7 +26,7 @@ export type AccelerationEstimate = {
|
|||||||
mempoolBaseFee: number;
|
mempoolBaseFee: number;
|
||||||
vsizeFee: number;
|
vsizeFee: number;
|
||||||
pools: number[];
|
pools: number[];
|
||||||
availablePaymentMethods: Record<PaymentMethod, {min: number, max: number, card?: {card_id: string, last_4: string, brand: string, name: string, billing: any}}>;
|
availablePaymentMethods: Record<PaymentMethod, {min: number, max: number}>;
|
||||||
unavailable?: boolean;
|
unavailable?: boolean;
|
||||||
options: { // recommended bid options
|
options: { // recommended bid options
|
||||||
fee: number; // recommended userBid in sats
|
fee: number; // recommended userBid in sats
|
||||||
@@ -49,7 +49,7 @@ export const MIN_BID_RATIO = 1;
|
|||||||
export const DEFAULT_BID_RATIO = 2;
|
export const DEFAULT_BID_RATIO = 2;
|
||||||
export const MAX_BID_RATIO = 4;
|
export const MAX_BID_RATIO = 4;
|
||||||
|
|
||||||
type CheckoutStep = 'quote' | 'summary' | 'checkout' | 'cashapp' | 'applepay' | 'googlepay' | 'cardonfile' | 'processing' | 'paid' | 'success';
|
type CheckoutStep = 'quote' | 'summary' | 'checkout' | 'cashapp' | 'applepay' | 'googlepay' | 'processing' | 'paid' | 'success';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-accelerate-checkout',
|
selector: 'app-accelerate-checkout',
|
||||||
@@ -65,7 +65,6 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
|
|||||||
@Input() cashappEnabled: boolean = true;
|
@Input() cashappEnabled: boolean = true;
|
||||||
@Input() applePayEnabled: boolean = false;
|
@Input() applePayEnabled: boolean = false;
|
||||||
@Input() googlePayEnabled: boolean = true;
|
@Input() googlePayEnabled: boolean = true;
|
||||||
@Input() cardOnFileEnabled: boolean = true;
|
|
||||||
@Input() advancedEnabled: boolean = false;
|
@Input() advancedEnabled: boolean = false;
|
||||||
@Input() forceMobile: boolean = false;
|
@Input() forceMobile: boolean = false;
|
||||||
@Input() showDetails: boolean = false;
|
@Input() showDetails: boolean = false;
|
||||||
@@ -118,7 +117,6 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
|
|||||||
loadingCashapp = false;
|
loadingCashapp = false;
|
||||||
loadingApplePay = false;
|
loadingApplePay = false;
|
||||||
loadingGooglePay = false;
|
loadingGooglePay = false;
|
||||||
loadingCardOnFile = false;
|
|
||||||
payments: any;
|
payments: any;
|
||||||
cashAppPay: any;
|
cashAppPay: any;
|
||||||
applePay: any;
|
applePay: any;
|
||||||
@@ -236,10 +234,6 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
|
|||||||
this.loadingGooglePay = true;
|
this.loadingGooglePay = true;
|
||||||
this.setupSquare();
|
this.setupSquare();
|
||||||
this.scrollToElementWithTimeout('confirm-title', 'center', 100);
|
this.scrollToElementWithTimeout('confirm-title', 'center', 100);
|
||||||
} else if (this._step === 'cardonfile' && this.cardOnFileEnabled) {
|
|
||||||
this.loadingCardOnFile = true;
|
|
||||||
this.setupSquare();
|
|
||||||
this.scrollToElementWithTimeout('confirm-title', 'center', 100);
|
|
||||||
} else if (this._step === 'paid') {
|
} else if (this._step === 'paid') {
|
||||||
this.timePaid = Date.now();
|
this.timePaid = Date.now();
|
||||||
this.timeoutTimer = setTimeout(() => {
|
this.timeoutTimer = setTimeout(() => {
|
||||||
@@ -460,8 +454,6 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
|
|||||||
await this.requestApplePayPayment();
|
await this.requestApplePayPayment();
|
||||||
} else if (this._step === 'googlepay') {
|
} else if (this._step === 'googlepay') {
|
||||||
await this.requestGooglePayPayment();
|
await this.requestGooglePayPayment();
|
||||||
} else if (this._step === 'cardonfile') {
|
|
||||||
this.loadingCardOnFile = false;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: () => {
|
error: () => {
|
||||||
@@ -718,109 +710,6 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Card On File
|
|
||||||
*/
|
|
||||||
async requestCardOnFilePayment(): Promise<void> {
|
|
||||||
if (this.processing) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.conversionsSubscription) {
|
|
||||||
this.conversionsSubscription.unsubscribe();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.processing = true;
|
|
||||||
this.conversionsSubscription = this.stateService.conversions$.subscribe(
|
|
||||||
async (conversions) => {
|
|
||||||
this.conversions = conversions;
|
|
||||||
|
|
||||||
const costUSD = this.cost / 100_000_000 * conversions.USD;
|
|
||||||
if (this.isCheckoutLocked > 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const cardOnFile = this.estimate?.availablePaymentMethods?.cardOnFile;
|
|
||||||
if (!cardOnFile?.card) {
|
|
||||||
this.accelerateError = 'card_on_file_not_found';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.loadingCardOnFile = false;
|
|
||||||
|
|
||||||
try {
|
|
||||||
this.isCheckoutLocked += 2;
|
|
||||||
this.isTokenizing += 2;
|
|
||||||
|
|
||||||
const nameParts = cardOnFile.card.name.split(' ');
|
|
||||||
const assumedGivenName = nameParts[0];
|
|
||||||
const assumedFamilyName = nameParts.length > 1 ? nameParts[1] : undefined;
|
|
||||||
const verificationDetails = {
|
|
||||||
card: {
|
|
||||||
billing: {
|
|
||||||
givenName: assumedGivenName,
|
|
||||||
familyName: assumedFamilyName,
|
|
||||||
addressLines: [cardOnFile.card.billing.addressLine1 ?? ''],
|
|
||||||
city: cardOnFile.card.billing.locality ?? '',
|
|
||||||
state: cardOnFile.card.billing.administrativeDistrictLevel1 ?? '',
|
|
||||||
countyCode: cardOnFile.card.billing.country,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const verificationToken = await this.$verifyBuyer(this.payments, cardOnFile.card.card_id, verificationDetails, costUSD.toFixed(2));
|
|
||||||
if (!verificationToken || !verificationToken.token) {
|
|
||||||
console.error(`SCA verification failed`);
|
|
||||||
this.accelerateError = 'SCA Verification Failed. Payment Declined.';
|
|
||||||
this.processing = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.servicesApiService.accelerateWithCardOnFile$(
|
|
||||||
this.tx.txid,
|
|
||||||
cardOnFile.card.card_id,
|
|
||||||
verificationToken.token,
|
|
||||||
`accelerator-${this.tx.txid.substring(0, 15)}-${Math.round(new Date().getTime() / 1000)}`,
|
|
||||||
costUSD,
|
|
||||||
verificationToken.userChallenged
|
|
||||||
).subscribe({
|
|
||||||
next: () => {
|
|
||||||
this.processing = false;
|
|
||||||
this.apiService.logAccelerationRequest$(this.tx.txid).subscribe();
|
|
||||||
this.audioService.playSound('ascend-chime-cartoon');
|
|
||||||
setTimeout(() => {
|
|
||||||
this.isCheckoutLocked--;
|
|
||||||
this.isTokenizing--;
|
|
||||||
this.moveToStep('paid', true);
|
|
||||||
}, 1000);
|
|
||||||
},
|
|
||||||
error: (response) => {
|
|
||||||
this.processing = false;
|
|
||||||
this.accelerateError = response.error;
|
|
||||||
this.isCheckoutLocked--;
|
|
||||||
this.isTokenizing--;
|
|
||||||
if (!(response.status === 403 && response.error === 'not_available')) {
|
|
||||||
setTimeout(() => {
|
|
||||||
// Reset everything by reloading the page :D, can be improved
|
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
|
||||||
window.location.assign(window.location.toString().replace(`?cash_request_id=${urlParams.get('cash_request_id')}`, ``));
|
|
||||||
}, 3000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e);
|
|
||||||
this.isCheckoutLocked--;
|
|
||||||
this.isTokenizing--;
|
|
||||||
this.processing = false;
|
|
||||||
this.accelerateError = e.message;
|
|
||||||
|
|
||||||
} finally {
|
|
||||||
// always unlock the checkout once we're finished
|
|
||||||
this.isCheckoutLocked--;
|
|
||||||
this.isTokenizing--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CASHAPP
|
* CASHAPP
|
||||||
*/
|
*/
|
||||||
@@ -1066,22 +955,6 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
get canPayWithCardOnFile(): boolean {
|
|
||||||
if (!this.cardOnFileEnabled || !this.conversions || (!this.isProdDomain && !isDevMode())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const paymentMethod = this.estimate?.availablePaymentMethods?.cardOnFile;
|
|
||||||
if (paymentMethod) {
|
|
||||||
const costUSD = (this.cost / 100_000_000 * this.conversions.USD);
|
|
||||||
if (costUSD >= paymentMethod.min && costUSD <= paymentMethod.max) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
get canPayWithBalance(): boolean {
|
get canPayWithBalance(): boolean {
|
||||||
if (!this.hasAccessToBalanceMode) {
|
if (!this.hasAccessToBalanceMode) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<th class="time text-right" i18n="accelerator.requested">Requested</th>
|
<th class="time text-right" i18n="accelerator.requested">Requested</th>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container *ngIf="!pending">
|
<ng-container *ngIf="!pending">
|
||||||
<th class="fee text-right text-truncate" i18n="transaction.bid-boost|Bid Boost">Bid Boost</th>
|
<th class="fee text-right" i18n="transaction.bid-boost|Bid Boost">Bid Boost</th>
|
||||||
<th class="block text-right" i18n="shared.block-title">Block</th>
|
<th class="block text-right" i18n="shared.block-title">Block</th>
|
||||||
<th class="pool text-right" i18n="mining.pool-name" *ngIf="!this.widget">Pool</th>
|
<th class="pool text-right" i18n="mining.pool-name" *ngIf="!this.widget">Pool</th>
|
||||||
<th class="status text-right" i18n="transaction.status|Transaction Status">Status</th>
|
<th class="status text-right" i18n="transaction.status|Transaction Status">Status</th>
|
||||||
@@ -64,8 +64,7 @@
|
|||||||
<span *ngIf="acceleration.status === 'accelerating'" class="badge badge-warning" i18n="accelerator.pending">Pending</span>
|
<span *ngIf="acceleration.status === 'accelerating'" class="badge badge-warning" i18n="accelerator.pending">Pending</span>
|
||||||
<span *ngIf="acceleration.status.includes('completed') && acceleration.minedByPoolUniqueId && pools[acceleration.minedByPoolUniqueId]" class="badge badge-success"><ng-container i18n="accelerator.completed">Completed</ng-container><span *ngIf="acceleration.status === 'completed_provisional'"> ⌛</span></span>
|
<span *ngIf="acceleration.status.includes('completed') && acceleration.minedByPoolUniqueId && pools[acceleration.minedByPoolUniqueId]" class="badge badge-success"><ng-container i18n="accelerator.completed">Completed</ng-container><span *ngIf="acceleration.status === 'completed_provisional'"> ⌛</span></span>
|
||||||
<span *ngIf="acceleration.status.includes('completed') && (!acceleration.minedByPoolUniqueId || !pools[acceleration.minedByPoolUniqueId])" class="badge badge-success"><ng-container i18n="transaction.rbf.mined">Mined</ng-container><span *ngIf="acceleration.status === 'completed_provisional'"> ⌛</span></span>
|
<span *ngIf="acceleration.status.includes('completed') && (!acceleration.minedByPoolUniqueId || !pools[acceleration.minedByPoolUniqueId])" class="badge badge-success"><ng-container i18n="transaction.rbf.mined">Mined</ng-container><span *ngIf="acceleration.status === 'completed_provisional'"> ⌛</span></span>
|
||||||
<span *ngIf="acceleration.status.includes('failed') && acceleration.canceled" class="badge badge-danger"><ng-container i18n="accelerator.canceled">Canceled</ng-container><span *ngIf="acceleration.status === 'failed_provisional'"> ⌛</span></span>
|
<span *ngIf="acceleration.status.includes('failed')" class="badge badge-danger"><ng-container i18n="accelerator.canceled">Canceled</ng-container><span *ngIf="acceleration.status === 'failed_provisional'"> ⌛</span></span>
|
||||||
<span *ngIf="acceleration.status.includes('failed') && !acceleration.canceled" class="badge badge-danger"><ng-container i18n="accelerator.canceled">Failed</ng-container><span *ngIf="acceleration.status === 'failed_provisional'"> ⌛</span></span>
|
|
||||||
</td>
|
</td>
|
||||||
<td class="date text-right" *ngIf="!this.widget">
|
<td class="date text-right" *ngIf="!this.widget">
|
||||||
<app-time kind="since" [time]="acceleration.added" [fastRender]="true" [showTooltip]="true"></app-time>
|
<app-time kind="since" [time]="acceleration.added" [fastRender]="true" [showTooltip]="true"></app-time>
|
||||||
|
|||||||
@@ -21,8 +21,10 @@
|
|||||||
<div class="alert alert-mempool d-block text-center w-100">
|
<div class="alert alert-mempool d-block text-center w-100">
|
||||||
<div class="d-inline align-middle">
|
<div class="d-inline align-middle">
|
||||||
<span>To use the faucet, please </span>
|
<span>To use the faucet, please </span>
|
||||||
|
<a routerLink="/login" [queryParams]="{'redirectTo': '/testnet4/faucet'}">login</a>
|
||||||
|
<span class="mr-2"> or</span>
|
||||||
</div>
|
</div>
|
||||||
<app-github-login customClass="btn btn-sm" width="220px" redirectTo="/testnet4/faucet" buttonString="Sign up with Github"></app-github-login>
|
<app-twitter-login customClass="btn btn-sm" width="220px" redirectTo="/testnet4/faucet" buttonString="Sign up with Twitter"></app-twitter-login>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@else if (user && user.status === 'pending' && !user.email && user.snsId) {
|
@else if (user && user.status === 'pending' && !user.email && user.snsId) {
|
||||||
@@ -34,18 +36,18 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@else if (error === 'not_available') {
|
@else if (error === 'not_available') {
|
||||||
<!-- User logged in but not a paid user or did not link its Github account -->
|
<!-- User logged in but not a paid user or did not link its Twitter account -->
|
||||||
<div class="alert alert-mempool d-block text-center w-100">
|
<div class="alert alert-mempool d-block text-center w-100">
|
||||||
<div class="d-inline align-middle">
|
<div class="d-inline align-middle">
|
||||||
<span class="mb-2 mr-2">To use the faucet, please</span>
|
<span class="mb-2 mr-2">To use the faucet, please</span>
|
||||||
</div>
|
</div>
|
||||||
<app-github-login customClass="btn btn-sm" width="180px" redirectTo="/testnet4/faucet" buttonString="Link your Github"></app-github-login>
|
<app-twitter-login customClass="btn btn-sm" width="180px" redirectTo="/testnet4/faucet" buttonString="Link your Twitter"></app-twitter-login>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@else if (error === 'account_limited') {
|
@else if (error === 'account_limited') {
|
||||||
<div class="alert alert-mempool d-block text-center w-100">
|
<div class="alert alert-mempool d-block text-center w-100">
|
||||||
<div class="d-inline align-middle">
|
<div class="d-inline align-middle">
|
||||||
<span class="mb-2 mr-2">Your account does not allow you to access the faucet</span>
|
<span class="mb-2 mr-2">Your Twitter account does not allow you to access the faucet</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
<a href="#" (click)="githubLogin()" [class]="(disabled ? 'disabled': '') + (customClass ? customClass : 'w-100 btn mt-1 d-flex justify-content-center align-items-center')" style="background-color: rgb(31, 35, 40)" [style]="width ? 'width: ' + width : ''">
|
|
||||||
<svg height="32" viewBox="0 0 18 16" width="32" style="fill: white;">
|
|
||||||
<path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/>
|
|
||||||
</svg>
|
|
||||||
<span class="ml-2 text-light align-middle">{{ buttonString }}</span>
|
|
||||||
</a>
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
|
||||||
@Component({
|
|
||||||
selector: 'app-github-login',
|
|
||||||
templateUrl: './github-login.component.html',
|
|
||||||
})
|
|
||||||
export class GithubLogin {
|
|
||||||
@Input() width: string | null = null;
|
|
||||||
@Input() customClass: string | null = null;
|
|
||||||
@Input() buttonString: string= 'unset';
|
|
||||||
@Input() redirectTo: string | null = null;
|
|
||||||
@Output() clicked = new EventEmitter<boolean>();
|
|
||||||
@Input() disabled: boolean = false;
|
|
||||||
|
|
||||||
constructor() {}
|
|
||||||
|
|
||||||
githubLogin() {
|
|
||||||
this.clicked.emit(true);
|
|
||||||
if (this.redirectTo) {
|
|
||||||
location.replace(`/api/v1/services/auth/login/github?redirectTo=${encodeURIComponent(this.redirectTo)}`);
|
|
||||||
} else {
|
|
||||||
location.replace(`/api/v1/services/auth/login/github?redirectTo=${location.href}`);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -267,11 +267,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
@for (branch of job.merkleBranches; track $index) {
|
@for (branch of job.merkleBranches; track $index) {
|
||||||
@if ($index === 0 && branch) {
|
<td class="merkle" [style.background-color]="branch ? '#' + branch.slice(0, 6) : ''"></td>
|
||||||
<a [routerLink]="['/tx' | relativeUrl, reverseHash(branch)]"><td class="merkle" [style.background-color]="branch ? '#' + branch.slice(0, 6) : ''"><fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true" style="vertical-align: text-top; font-size: 14px; color: white"></fa-icon></td></a>
|
|
||||||
} @else {
|
|
||||||
<td class="merkle" [style.background-color]="branch ? '#' + branch.slice(0, 6) : ''"></td>
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@for (_ of [].constructor(Math.max(0, 12 - job.merkleBranches.length)); track $index) {
|
@for (_ of [].constructor(Math.max(0, 12 - job.merkleBranches.length)); track $index) {
|
||||||
<td class="merkle empty-branch"></td>
|
<td class="merkle empty-branch"></td>
|
||||||
|
|||||||
@@ -220,7 +220,6 @@ div.scrollable {
|
|||||||
|
|
||||||
.merkle {
|
.merkle {
|
||||||
width: 100px;
|
width: 100px;
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty-branch {
|
.empty-branch {
|
||||||
|
|||||||
@@ -361,10 +361,6 @@ export class PoolComponent implements OnInit {
|
|||||||
return block.height;
|
return block.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
reverseHash(hash: string) {
|
|
||||||
return hash.match(/../g).reverse().join('');
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
this.slugSubscription.unsubscribe();
|
this.slugSubscription.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,27 +7,30 @@
|
|||||||
<table *ngIf="poolsReady && (rows$ | async) as rows;" class="stratum-table">
|
<table *ngIf="poolsReady && (rows$ | async) as rows;" class="stratum-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
<td class="height">Height</td>
|
||||||
|
<td class="reward">Reward</td>
|
||||||
|
<td class="tag">Coinbase Tag</td>
|
||||||
<td class="merkle" [attr.colspan]="rows[0]?.merkleCells?.length || 4">
|
<td class="merkle" [attr.colspan]="rows[0]?.merkleCells?.length || 4">
|
||||||
Merkle Branches
|
Merkle Branches
|
||||||
</td>
|
</td>
|
||||||
<td class="pool">Pool</td>
|
<td class="pool">Pool</td>
|
||||||
<td class="tag">Coinbase Tag</td>
|
|
||||||
<td class="reward">Reward</td>
|
|
||||||
<td class="height">Height</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@for (row of rows; track row.job.pool) {
|
@for (row of rows; track row.job.pool) {
|
||||||
<tr>
|
<tr>
|
||||||
|
<td class="height">
|
||||||
|
{{ row.job.height }}
|
||||||
|
</td>
|
||||||
|
<td class="reward">
|
||||||
|
<app-amount [satoshis]="row.job.reward"></app-amount>
|
||||||
|
</td>
|
||||||
|
<td class="tag">
|
||||||
|
{{ row.job.tag }}
|
||||||
|
</td>
|
||||||
@for (cell of row.merkleCells; track $index) {
|
@for (cell of row.merkleCells; track $index) {
|
||||||
<td class="merkle" [style.background-color]="cell.hash ? '#' + cell.hash.slice(0, 6) : ''">
|
<td class="merkle" [style.background-color]="cell.hash ? '#' + cell.hash.slice(0, 6) : ''">
|
||||||
@if ($index === 0 && cell.hash) {
|
<div class="pipe-segment" [class]="pipeToClass(cell.type)"></div>
|
||||||
<a [routerLink]="['/tx' | relativeUrl, reverseHash(cell.hash)]" class="cell-link">
|
|
||||||
<div class="pipe-segment" [class]="pipeToClass(cell.type)"></div>
|
|
||||||
</a>
|
|
||||||
} @else {
|
|
||||||
<div class="pipe-segment" [class]="pipeToClass(cell.type)"></div>
|
|
||||||
}
|
|
||||||
</td>
|
</td>
|
||||||
}
|
}
|
||||||
<td class="pool">
|
<td class="pool">
|
||||||
@@ -38,15 +41,6 @@
|
|||||||
</a>
|
</a>
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
<td class="tag">
|
|
||||||
{{ row.job.tag }}
|
|
||||||
</td>
|
|
||||||
<td class="reward">
|
|
||||||
<app-amount [satoshis]="row.job.reward"></app-amount>
|
|
||||||
</td>
|
|
||||||
<td class="height">
|
|
||||||
{{ row.job.height }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@@ -92,36 +92,6 @@ td {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.cell-link {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
color: inherit;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 800px) {
|
|
||||||
.stratum-table {
|
|
||||||
td {
|
|
||||||
&.tag {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 650px) {
|
|
||||||
.stratum-table {
|
|
||||||
td {
|
|
||||||
&.reward {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge {
|
.badge {
|
||||||
|
|||||||
@@ -48,16 +48,14 @@ function parseTag(scriptSig: string): string {
|
|||||||
return (ascii.match(/\/.*\//)?.[0] || ascii).trim();
|
return (ascii.match(/\/.*\//)?.[0] || ascii).trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMerkleBranchIds(merkleBranches: string[], numBranches: number, poolId: number): string[] {
|
function getMerkleBranchIds(merkleBranches: string[], numBranches: number): string[] {
|
||||||
let lastHash = '';
|
let lastHash = '';
|
||||||
const ids: string[] = [];
|
const ids: string[] = [];
|
||||||
for (let i = 0; i < numBranches; i++) {
|
for (let i = 0; i < numBranches; i++) {
|
||||||
if (merkleBranches[i]) {
|
if (merkleBranches[i]) {
|
||||||
lastHash = merkleBranches[i];
|
lastHash = merkleBranches[i];
|
||||||
ids.push(`${i}-${lastHash}`);
|
|
||||||
} else {
|
|
||||||
ids.push(`${i}-${lastHash}-${poolId}`);
|
|
||||||
}
|
}
|
||||||
|
ids.push(`${i}-${lastHash}`);
|
||||||
}
|
}
|
||||||
return ids;
|
return ids;
|
||||||
}
|
}
|
||||||
@@ -100,7 +98,7 @@ export class StratumList implements OnInit, OnDestroy {
|
|||||||
const numBranches = Math.max(...Object.values(rawJobs).map(job => job.merkleBranches.length));
|
const numBranches = Math.max(...Object.values(rawJobs).map(job => job.merkleBranches.length));
|
||||||
const jobs: Record<string, TaggedStratumJob> = {};
|
const jobs: Record<string, TaggedStratumJob> = {};
|
||||||
for (const [id, job] of Object.entries(rawJobs)) {
|
for (const [id, job] of Object.entries(rawJobs)) {
|
||||||
jobs[id] = { ...job, tag: parseTag(job.scriptsig), merkleBranchIds: getMerkleBranchIds(job.merkleBranches, numBranches, job.pool) };
|
jobs[id] = { ...job, tag: parseTag(job.scriptsig), merkleBranchIds: getMerkleBranchIds(job.merkleBranches, numBranches) };
|
||||||
}
|
}
|
||||||
if (Object.keys(jobs).length === 0) {
|
if (Object.keys(jobs).length === 0) {
|
||||||
return [];
|
return [];
|
||||||
@@ -198,10 +196,6 @@ export class StratumList implements OnInit, OnDestroy {
|
|||||||
}[type];
|
}[type];
|
||||||
}
|
}
|
||||||
|
|
||||||
reverseHash(hash: string) {
|
|
||||||
return hash.match(/../g).reverse().join('');
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
this.websocketService.stopTrackStratum();
|
this.websocketService.stopTrackStratum();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -412,13 +412,13 @@ export interface Acceleration {
|
|||||||
feeDelta: number;
|
feeDelta: number;
|
||||||
blockHash: string;
|
blockHash: string;
|
||||||
blockHeight: number;
|
blockHeight: number;
|
||||||
|
|
||||||
acceleratedFeeRate?: number;
|
acceleratedFeeRate?: number;
|
||||||
boost?: number;
|
boost?: number;
|
||||||
bidBoost?: number;
|
bidBoost?: number;
|
||||||
boostCost?: number;
|
boostCost?: number;
|
||||||
boostRate?: number;
|
boostRate?: number;
|
||||||
minedByPoolUniqueId?: number;
|
minedByPoolUniqueId?: number;
|
||||||
canceled?: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AccelerationHistoryParams {
|
export interface AccelerationHistoryParams {
|
||||||
|
|||||||
@@ -58,7 +58,6 @@ export class AuthServiceMempool {
|
|||||||
setAuth(auth: any) {
|
setAuth(auth: any) {
|
||||||
if (!auth) {
|
if (!auth) {
|
||||||
localStorage.removeItem('auth');
|
localStorage.removeItem('auth');
|
||||||
localStorage.removeItem('authenticatorStatus');
|
|
||||||
} else {
|
} else {
|
||||||
localStorage.setItem('auth', JSON.stringify(auth));
|
localStorage.setItem('auth', JSON.stringify(auth));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -146,10 +146,6 @@ export class ServicesApiServices {
|
|||||||
return this.httpClient.post<any>(`${this.stateService.env.SERVICES_API}/accelerator/accelerate/googlePay`, { txInput: txInput, cardTag: cardTag, token: token, verificationToken: verificationToken, referenceId: referenceId, userApprovedUSD: userApprovedUSD, userChallenged: userChallenged });
|
return this.httpClient.post<any>(`${this.stateService.env.SERVICES_API}/accelerator/accelerate/googlePay`, { txInput: txInput, cardTag: cardTag, token: token, verificationToken: verificationToken, referenceId: referenceId, userApprovedUSD: userApprovedUSD, userChallenged: userChallenged });
|
||||||
}
|
}
|
||||||
|
|
||||||
accelerateWithCardOnFile$(txInput: string, token: string, verificationToken: string, referenceId: string, userApprovedUSD: number, userChallenged: boolean) {
|
|
||||||
return this.httpClient.post<any>(`${this.stateService.env.SERVICES_API}/accelerator/accelerate/cardOnFile`, { txInput: txInput, token: token, verificationToken: verificationToken, referenceId: referenceId, userApprovedUSD: userApprovedUSD, userChallenged: userChallenged });
|
|
||||||
}
|
|
||||||
|
|
||||||
getAccelerations$(): Observable<Acceleration[]> {
|
getAccelerations$(): Observable<Acceleration[]> {
|
||||||
return this.httpClient.get<Acceleration[]>(`${this.stateService.env.SERVICES_API}/accelerator/accelerations`);
|
return this.httpClient.get<Acceleration[]>(`${this.stateService.env.SERVICES_API}/accelerator/accelerations`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { faFilter, faAngleDown, faAngleUp, faAngleRight, faAngleLeft, faBolt, fa
|
|||||||
faFileAlt, faRedoAlt, faArrowAltCircleRight, faExternalLinkAlt, faBook, faListUl, faDownload, faQrcode, faArrowRightArrowLeft, faArrowsRotate, faCircleLeft,
|
faFileAlt, faRedoAlt, faArrowAltCircleRight, faExternalLinkAlt, faBook, faListUl, faDownload, faQrcode, faArrowRightArrowLeft, faArrowsRotate, faCircleLeft,
|
||||||
faFastForward, faWallet, faUserClock, faWrench, faUserFriends, faQuestionCircle, faHistory, faSignOutAlt, faKey, faSuitcase, faIdCardAlt, faNetworkWired, faUserCheck,
|
faFastForward, faWallet, faUserClock, faWrench, faUserFriends, faQuestionCircle, faHistory, faSignOutAlt, faKey, faSuitcase, faIdCardAlt, faNetworkWired, faUserCheck,
|
||||||
faCircleCheck, faUserCircle, faCheck, faRocket, faScaleBalanced, faHourglassStart, faHourglassHalf, faHourglassEnd, faWandMagicSparkles, faFaucetDrip, faTimeline,
|
faCircleCheck, faUserCircle, faCheck, faRocket, faScaleBalanced, faHourglassStart, faHourglassHalf, faHourglassEnd, faWandMagicSparkles, faFaucetDrip, faTimeline,
|
||||||
faCircleXmark, faCalendarCheck, faMoneyBillTrendUp, faRobot, faShareNodes, faCreditCard } from '@fortawesome/free-solid-svg-icons';
|
faCircleXmark, faCalendarCheck, faMoneyBillTrendUp, faRobot, faShareNodes } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
|
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
|
||||||
import { MenuComponent } from '@components/menu/menu.component';
|
import { MenuComponent } from '@components/menu/menu.component';
|
||||||
import { PreviewTitleComponent } from '@components/master-page-preview/preview-title.component';
|
import { PreviewTitleComponent } from '@components/master-page-preview/preview-title.component';
|
||||||
@@ -125,7 +125,6 @@ import { TwitterLogin } from '@components/twitter-login/twitter-login.component'
|
|||||||
import { BitcoinInvoiceComponent } from '@components/bitcoin-invoice/bitcoin-invoice.component';
|
import { BitcoinInvoiceComponent } from '@components/bitcoin-invoice/bitcoin-invoice.component';
|
||||||
|
|
||||||
import { OnlyVsizeDirective, OnlyWeightDirective } from '@app/shared/components/weight-directives/weight-directives';
|
import { OnlyVsizeDirective, OnlyWeightDirective } from '@app/shared/components/weight-directives/weight-directives';
|
||||||
import { GithubLogin } from '../components/github-login.component/github-login.component';
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@@ -243,7 +242,6 @@ import { GithubLogin } from '../components/github-login.component/github-login.c
|
|||||||
TwitterWidgetComponent,
|
TwitterWidgetComponent,
|
||||||
FaucetComponent,
|
FaucetComponent,
|
||||||
TwitterLogin,
|
TwitterLogin,
|
||||||
GithubLogin,
|
|
||||||
BitcoinInvoiceComponent,
|
BitcoinInvoiceComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
@@ -378,7 +376,6 @@ import { GithubLogin } from '../components/github-login.component/github-login.c
|
|||||||
HttpErrorComponent,
|
HttpErrorComponent,
|
||||||
TwitterWidgetComponent,
|
TwitterWidgetComponent,
|
||||||
TwitterLogin,
|
TwitterLogin,
|
||||||
GithubLogin,
|
|
||||||
BitcoinInvoiceComponent,
|
BitcoinInvoiceComponent,
|
||||||
BitcoinsatoshisPipe,
|
BitcoinsatoshisPipe,
|
||||||
|
|
||||||
@@ -463,6 +460,5 @@ export class SharedModule {
|
|||||||
library.addIcons(faMoneyBillTrendUp);
|
library.addIcons(faMoneyBillTrendUp);
|
||||||
library.addIcons(faRobot);
|
library.addIcons(faRobot);
|
||||||
library.addIcons(faShareNodes);
|
library.addIcons(faShareNodes);
|
||||||
library.addIcons(faCreditCard);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,21 +3,11 @@
|
|||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
|
|
||||||
<!-- LOAD BEARING COMMENTS - DO NOT TOUCH! -->
|
|
||||||
<!-- TITLE -->
|
|
||||||
<title>mempool - Bitcoin Explorer</title>
|
<title>mempool - Bitcoin Explorer</title>
|
||||||
<!-- END TITLE -->
|
|
||||||
|
|
||||||
<!-- CONFIG -->
|
|
||||||
<script src="/resources/config.js"></script>
|
<script src="/resources/config.js"></script>
|
||||||
<!-- END CONFIG -->
|
|
||||||
<!-- CUSTOMIZATION -->
|
|
||||||
<script src="/resources/customize.js"></script>
|
<script src="/resources/customize.js"></script>
|
||||||
<!-- END CUSTOMIZATION -->
|
|
||||||
<base href="/">
|
<base href="/">
|
||||||
|
|
||||||
<!-- META -->
|
|
||||||
<meta name="description" content="Explore the full Bitcoin ecosystem with The Mempool Open Source Project®. See the real-time status of your transactions, get network info, and more." />
|
<meta name="description" content="Explore the full Bitcoin ecosystem with The Mempool Open Source Project®. See the real-time status of your transactions, get network info, and more." />
|
||||||
<meta property="og:image" content="https://mempool.space/resources/previews/mempool-space-preview.jpg" />
|
<meta property="og:image" content="https://mempool.space/resources/previews/mempool-space-preview.jpg" />
|
||||||
<meta property="og:image:type" content="image/jpeg" />
|
<meta property="og:image:type" content="image/jpeg" />
|
||||||
@@ -31,21 +21,19 @@
|
|||||||
<meta name="twitter:description" content="Explore the full Bitcoin ecosystem with The Mempool Open Source Project®. See the real-time status of your transactions, get network info, and more." />
|
<meta name="twitter:description" content="Explore the full Bitcoin ecosystem with The Mempool Open Source Project®. See the real-time status of your transactions, get network info, and more." />
|
||||||
<meta name="twitter:image" content="https://mempool.space/resources/previews/mempool-space-preview.jpg" />
|
<meta name="twitter:image" content="https://mempool.space/resources/previews/mempool-space-preview.jpg" />
|
||||||
<meta name="twitter:domain" content="mempool.space">
|
<meta name="twitter:domain" content="mempool.space">
|
||||||
<!-- END META -->
|
|
||||||
|
|
||||||
<!-- FAVICONS -->
|
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/resources/favicons/apple-touch-icon.png">
|
<link rel="apple-touch-icon" sizes="180x180" href="/resources/favicons/apple-touch-icon.png">
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="/resources/favicons/favicon-32x32.png">
|
<link rel="icon" type="image/png" sizes="32x32" href="/resources/favicons/favicon-32x32.png">
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="/resources/favicons/favicon-16x16.png">
|
<link rel="icon" type="image/png" sizes="16x16" href="/resources/favicons/favicon-16x16.png">
|
||||||
<link rel="manifest" href="/resources/favicons/site.webmanifest">
|
<link rel="manifest" href="/resources/favicons/site.webmanifest">
|
||||||
<link rel="shortcut icon" href="/resources/favicons/favicon.ico">
|
<link rel="shortcut icon" href="/resources/favicons/favicon.ico">
|
||||||
<link id="canonical" rel="canonical" href="https://mempool.space">
|
<link id="canonical" rel="canonical" href="https://mempool.space">
|
||||||
|
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||||
<meta name="msapplication-TileColor" content="#000000">
|
<meta name="msapplication-TileColor" content="#000000">
|
||||||
<meta name="msapplication-config" content="/resources/favicons/browserconfig.xml">
|
<meta name="msapplication-config" content="/resources/favicons/browserconfig.xml">
|
||||||
<meta name="theme-color" content="#1d1f31">
|
<meta name="theme-color" content="#1d1f31">
|
||||||
<!-- END FAVICONS -->
|
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
@@ -1,128 +0,0 @@
|
|||||||
import fs from 'fs';
|
|
||||||
|
|
||||||
const defaultConfig = {
|
|
||||||
"domains": ["mempool.space"],
|
|
||||||
"theme": "default",
|
|
||||||
"enterprise": "mempool",
|
|
||||||
"branding": {
|
|
||||||
"name": "mempool",
|
|
||||||
"title": "mempool",
|
|
||||||
"site_id": 5,
|
|
||||||
},
|
|
||||||
"meta": {
|
|
||||||
"title": "mempool - Bitcoin Explorer",
|
|
||||||
"description": "Explore the full Bitcoin ecosystem with The Mempool Open Source Project®. See the real-time status of your transactions, get network info, and more.",
|
|
||||||
},
|
|
||||||
"unfurls": {
|
|
||||||
"preview": {
|
|
||||||
"src": "https://mempool.space/resources/previews/mempool-space-preview.jpg",
|
|
||||||
"type": "image/jpeg",
|
|
||||||
"width": "2000",
|
|
||||||
"height": "1000"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function deepMerge(target, source) {
|
|
||||||
for (const key in source) {
|
|
||||||
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
|
|
||||||
target[key] = target[key] || {};
|
|
||||||
deepMerge(target[key], source[key]);
|
|
||||||
} else {
|
|
||||||
target[key] = source[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
|
|
||||||
function addDefaultsToConfig(config) {
|
|
||||||
return deepMerge(structuredClone(defaultConfig), config);
|
|
||||||
}
|
|
||||||
|
|
||||||
function substitute(indexhtml, config) {
|
|
||||||
let newhtml = indexhtml;
|
|
||||||
// substitute title
|
|
||||||
newhtml = newhtml.replace(/\<\!\-\- TITLE \-\-\>.*\<\!\-\- END TITLE \-\-\>/gis, `<title>${config.meta.title}</title>`);
|
|
||||||
|
|
||||||
// substitute customization script
|
|
||||||
newhtml = newhtml.replace(/\<\!\-\- CUSTOMIZATION \-\-\>.*\<\!\-\- END CUSTOMIZATION \-\-\>/gis, `<script>
|
|
||||||
window.__env = window.__env || {};
|
|
||||||
window.__env.CUSTOMIZATION = 'auto';
|
|
||||||
window.__env.customize = ${JSON.stringify(config)};
|
|
||||||
</script>`);
|
|
||||||
|
|
||||||
// substitute meta tags
|
|
||||||
newhtml = newhtml.replace(/\<\!\-\- META \-\-\>.*\<\!\-\- END META \-\-\>/gis, `<meta name="description" content="${config.meta.description}" />
|
|
||||||
<meta property="og:image" content="${config.unfurls.preview.src}" />
|
|
||||||
<meta property="og:image:type" content="${config.unfurls.preview.type}" />
|
|
||||||
<meta property="og:image:width" content="${config.unfurls.preview.width}" />
|
|
||||||
<meta property="og:image:height" content="${config.unfurls.preview.height}" />
|
|
||||||
<meta property="og:description" content="${config.meta.description}" />
|
|
||||||
<meta name="twitter:card" content="summary_large_image">
|
|
||||||
<meta name="twitter:site" content="@mempool">
|
|
||||||
<meta name="twitter:creator" content="@mempool">
|
|
||||||
<meta name="twitter:title" content="${config.meta.title}">
|
|
||||||
<meta name="twitter:description" content="${config.meta.description}" />
|
|
||||||
<meta name="twitter:image" content="${config.unfurls.preview.src}" />
|
|
||||||
<meta name="twitter:domain" content="${config.domains[0]}">`);
|
|
||||||
|
|
||||||
|
|
||||||
// substitute favicons
|
|
||||||
newhtml = newhtml.replace(/\<\!\-\- FAVICONS -->.*\<\!\-\- END FAVICONS \-\-\>/gis, `<link rel="apple-touch-icon" sizes="180x180" href="/resources/${config.enterprise}/favicons/apple-touch-icon.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="/resources/${config.enterprise}/favicons/favicon-32x32.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="/resources/${config.enterprise}/favicons/favicon-16x16.png">
|
|
||||||
<link rel="manifest" href="/resources/${config.enterprise}/favicons/site.webmanifest">
|
|
||||||
<link rel="shortcut icon" href="/resources/${config.enterprise}/favicons/favicon.ico">
|
|
||||||
<link id="canonical" rel="canonical" href="${config.domains[0]}">
|
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
|
||||||
<meta name="msapplication-TileColor" content="#000000">
|
|
||||||
<meta name="msapplication-config" content="/resources/${config.enterprise}/favicons/browserconfig.xml">
|
|
||||||
<meta name="theme-color" content="#1d1f31">`);
|
|
||||||
|
|
||||||
return newhtml;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function run() {
|
|
||||||
const servicesHost = process.argv[2] || 'http://localhost:9000';
|
|
||||||
const mempoolDir = process.argv[3] || '../';
|
|
||||||
|
|
||||||
console.log('fetching list of custom builds');
|
|
||||||
const customBuilds = await (await fetch(`${servicesHost}/api/v1/services/custom/list`)).json();
|
|
||||||
|
|
||||||
// fetch config for each custom build from `$SERVICES/api/v1/services/custom/config/<custom_build_id>`
|
|
||||||
const customConfigs = await Promise.all(customBuilds.map(async (build) => {
|
|
||||||
console.log(`fetching config for ${build} `);
|
|
||||||
return addDefaultsToConfig(await (await fetch(`${servicesHost}/api/v1/services/custom/config/${build}`)).json());
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
|
||||||
// for each custom build config:
|
|
||||||
let i = 0;
|
|
||||||
for (const config of customConfigs) {
|
|
||||||
console.log(`generating ${config.enterprise} build (${i + 1}/${customConfigs.length})`);
|
|
||||||
const browserDir = `${mempoolDir}/frontend/dist/mempool/browser`;
|
|
||||||
const locales = fs.readdirSync(browserDir)
|
|
||||||
.filter(file => fs.statSync(`${browserDir}/${file}`).isDirectory())
|
|
||||||
.filter(file => fs.existsSync(`${browserDir}/${file}/index.html`));
|
|
||||||
|
|
||||||
// Process each locale's index.html
|
|
||||||
for (const locale of locales) {
|
|
||||||
const indexPath = `${browserDir}/${locale}/index.html`;
|
|
||||||
const indexContent = fs.readFileSync(indexPath, 'utf-8').toString();
|
|
||||||
const processedHtml = substitute(indexContent, config);
|
|
||||||
|
|
||||||
// Save processed HTML
|
|
||||||
const outputPath = `${browserDir}/${locale}/index.${config.enterprise}.html`;
|
|
||||||
fs.writeFileSync(outputPath, processedHtml);
|
|
||||||
console.log(`updated index.${config.enterprise}.html for locale ${locale}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`finished generating ${config.enterprise} build`);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('finished updating custom builds');
|
|
||||||
}
|
|
||||||
|
|
||||||
run();
|
|
||||||
Reference in New Issue
Block a user