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/')"
|
||||
strategy:
|
||||
matrix:
|
||||
node: ["20", "21"]
|
||||
node: ["20", "22"]
|
||||
flavor: ["dev", "prod"]
|
||||
fail-fast: false
|
||||
runs-on: "ubuntu-latest"
|
||||
@@ -160,7 +160,7 @@ jobs:
|
||||
if: "!contains(github.event.pull_request.labels.*.name, 'ops') && !contains(github.head_ref, 'ops/')"
|
||||
strategy:
|
||||
matrix:
|
||||
node: ["20", "21"]
|
||||
node: ["20", "22"]
|
||||
flavor: ["dev", "prod"]
|
||||
fail-fast: false
|
||||
runs-on: "ubuntu-latest"
|
||||
@@ -263,7 +263,7 @@ jobs:
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: 22
|
||||
cache: "npm"
|
||||
cache-dependency-path: ${{ matrix.module }}/frontend/package-lock.json
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ const config: Config.InitialOptions = {
|
||||
automock: false,
|
||||
collectCoverage: true,
|
||||
collectCoverageFrom: ["./src/**/**.ts"],
|
||||
coverageProvider: "v8",
|
||||
coverageProvider: "babel",
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
lines: 1
|
||||
|
||||
55
backend/package-lock.json
generated
55
backend/package-lock.json
generated
@@ -10,6 +10,7 @@
|
||||
"hasInstallScript": true,
|
||||
"license": "GNU Affero General Public License v3.0",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.25.2",
|
||||
"@mempool/electrum-client": "1.1.9",
|
||||
"@types/node": "^18.15.3",
|
||||
"axios": "1.7.2",
|
||||
@@ -17,7 +18,7 @@
|
||||
"crypto-js": "~4.2.0",
|
||||
"express": "~4.21.1",
|
||||
"maxmind": "~4.3.11",
|
||||
"mysql2": "~3.12.0",
|
||||
"mysql2": "~3.11.0",
|
||||
"redis": "^4.7.0",
|
||||
"rust-gbt": "file:./rust-gbt",
|
||||
"socks-proxy-agent": "~7.0.0",
|
||||
@@ -25,6 +26,8 @@
|
||||
"ws": "~8.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/code-frame": "^7.18.6",
|
||||
"@babel/core": "^7.25.2",
|
||||
"@types/compression": "^1.7.2",
|
||||
"@types/crypto-js": "^4.1.1",
|
||||
"@types/express": "^4.17.17",
|
||||
@@ -5997,21 +6000,6 @@
|
||||
"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": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
|
||||
@@ -6173,17 +6161,16 @@
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"node_modules/mysql2": {
|
||||
"version": "3.12.0",
|
||||
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.12.0.tgz",
|
||||
"integrity": "sha512-C8fWhVysZoH63tJbX8d10IAoYCyXy4fdRFz2Ihrt9jtPILYynFEKUUzpp1U7qxzDc3tMbotvaBH+sl6bFnGZiw==",
|
||||
"license": "MIT",
|
||||
"version": "3.11.0",
|
||||
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.11.0.tgz",
|
||||
"integrity": "sha512-J9phbsXGvTOcRVPR95YedzVSxJecpW5A5+cQ57rhHIFXteTP10HCs+VBjS7DHIKfEaI1zQ5tlVrquCd64A6YvA==",
|
||||
"dependencies": {
|
||||
"aws-ssl-profiles": "^1.1.1",
|
||||
"denque": "^2.1.0",
|
||||
"generate-function": "^2.3.1",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"long": "^5.2.1",
|
||||
"lru.min": "^1.0.0",
|
||||
"lru-cache": "^8.0.0",
|
||||
"named-placeholders": "^1.1.3",
|
||||
"seq-queue": "^0.0.5",
|
||||
"sqlstring": "^2.3.2"
|
||||
@@ -6203,6 +6190,14 @@
|
||||
"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": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz",
|
||||
@@ -12218,11 +12213,6 @@
|
||||
"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": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
|
||||
@@ -12337,16 +12327,16 @@
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"mysql2": {
|
||||
"version": "3.12.0",
|
||||
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.12.0.tgz",
|
||||
"integrity": "sha512-C8fWhVysZoH63tJbX8d10IAoYCyXy4fdRFz2Ihrt9jtPILYynFEKUUzpp1U7qxzDc3tMbotvaBH+sl6bFnGZiw==",
|
||||
"version": "3.11.0",
|
||||
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.11.0.tgz",
|
||||
"integrity": "sha512-J9phbsXGvTOcRVPR95YedzVSxJecpW5A5+cQ57rhHIFXteTP10HCs+VBjS7DHIKfEaI1zQ5tlVrquCd64A6YvA==",
|
||||
"requires": {
|
||||
"aws-ssl-profiles": "^1.1.1",
|
||||
"denque": "^2.1.0",
|
||||
"generate-function": "^2.3.1",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"long": "^5.2.1",
|
||||
"lru.min": "^1.0.0",
|
||||
"lru-cache": "^8.0.0",
|
||||
"named-placeholders": "^1.1.3",
|
||||
"seq-queue": "^0.0.5",
|
||||
"sqlstring": "^2.3.2"
|
||||
@@ -12359,6 +12349,11 @@
|
||||
"requires": {
|
||||
"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}\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.25.2",
|
||||
"@mempool/electrum-client": "1.1.9",
|
||||
"@types/node": "^18.15.3",
|
||||
"axios": "1.7.2",
|
||||
@@ -46,7 +47,7 @@
|
||||
"crypto-js": "~4.2.0",
|
||||
"express": "~4.21.1",
|
||||
"maxmind": "~4.3.11",
|
||||
"mysql2": "~3.12.0",
|
||||
"mysql2": "~3.11.0",
|
||||
"rust-gbt": "file:./rust-gbt",
|
||||
"redis": "^4.7.0",
|
||||
"socks-proxy-agent": "~7.0.0",
|
||||
@@ -54,6 +55,8 @@
|
||||
"ws": "~8.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/code-frame": "^7.18.6",
|
||||
"@babel/core": "^7.25.2",
|
||||
"@types/compression": "^1.7.2",
|
||||
"@types/crypto-js": "^4.1.1",
|
||||
"@types/express": "^4.17.17",
|
||||
|
||||
@@ -45,7 +45,6 @@ __SERVICES_API__=${SERVICES_API:=https://mempool.space/api/v1/services}
|
||||
__PUBLIC_ACCELERATIONS__=${PUBLIC_ACCELERATIONS:=false}
|
||||
__HISTORICAL_PRICE__=${HISTORICAL_PRICE:=true}
|
||||
__ADDITIONAL_CURRENCIES__=${ADDITIONAL_CURRENCIES:=false}
|
||||
__STRATUM_ENABLED__=${STRATUM_ENABLED:=false}
|
||||
|
||||
# Export as environment variables to be used by envsubst
|
||||
export __MAINNET_ENABLED__
|
||||
@@ -77,7 +76,6 @@ export __SERVICES_API__
|
||||
export __PUBLIC_ACCELERATIONS__
|
||||
export __HISTORICAL_PRICE__
|
||||
export __ADDITIONAL_CURRENCIES__
|
||||
export __STRATUM_ENABLED__
|
||||
|
||||
folder=$(find /var/www/mempool -name "config.js" | xargs dirname)
|
||||
echo ${folder}
|
||||
|
||||
30
frontend/package-lock.json
generated
30
frontend/package-lock.json
generated
@@ -33,7 +33,7 @@
|
||||
"browserify": "^17.0.0",
|
||||
"clipboard": "^2.0.11",
|
||||
"domino": "^2.1.6",
|
||||
"echarts": "~5.6.0",
|
||||
"echarts": "~5.5.0",
|
||||
"esbuild": "^0.24.0",
|
||||
"ngx-echarts": "~17.2.0",
|
||||
"ngx-infinite-scroll": "^17.0.0",
|
||||
@@ -8724,12 +8724,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/echarts": {
|
||||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmjs.org/echarts/-/echarts-5.6.0.tgz",
|
||||
"integrity": "sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==",
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/echarts/-/echarts-5.5.0.tgz",
|
||||
"integrity": "sha512-rNYnNCzqDAPCr4m/fqyUFv7fD9qIsd50S6GDFgO1DxZhncCsNsG7IfUlAlvZe5oSEQxtsjnHiUuppzccry93Xw==",
|
||||
"dependencies": {
|
||||
"tslib": "2.3.0",
|
||||
"zrender": "5.6.1"
|
||||
"zrender": "5.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/echarts/node_modules/tslib": {
|
||||
@@ -18366,9 +18366,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/zrender": {
|
||||
"version": "5.6.1",
|
||||
"resolved": "https://registry.npmjs.org/zrender/-/zrender-5.6.1.tgz",
|
||||
"integrity": "sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==",
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/zrender/-/zrender-5.5.0.tgz",
|
||||
"integrity": "sha512-O3MilSi/9mwoovx77m6ROZM7sXShR/O/JIanvzTwjN3FORfLSr81PsUGd7jlaYOeds9d8tw82oP44+3YucVo+w==",
|
||||
"dependencies": {
|
||||
"tslib": "2.3.0"
|
||||
}
|
||||
@@ -24485,12 +24485,12 @@
|
||||
}
|
||||
},
|
||||
"echarts": {
|
||||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmjs.org/echarts/-/echarts-5.6.0.tgz",
|
||||
"integrity": "sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==",
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/echarts/-/echarts-5.5.0.tgz",
|
||||
"integrity": "sha512-rNYnNCzqDAPCr4m/fqyUFv7fD9qIsd50S6GDFgO1DxZhncCsNsG7IfUlAlvZe5oSEQxtsjnHiUuppzccry93Xw==",
|
||||
"requires": {
|
||||
"tslib": "2.3.0",
|
||||
"zrender": "5.6.1"
|
||||
"zrender": "5.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": {
|
||||
@@ -31485,9 +31485,9 @@
|
||||
}
|
||||
},
|
||||
"zrender": {
|
||||
"version": "5.6.1",
|
||||
"resolved": "https://registry.npmjs.org/zrender/-/zrender-5.6.1.tgz",
|
||||
"integrity": "sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==",
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/zrender/-/zrender-5.5.0.tgz",
|
||||
"integrity": "sha512-O3MilSi/9mwoovx77m6ROZM7sXShR/O/JIanvzTwjN3FORfLSr81PsUGd7jlaYOeds9d8tw82oP44+3YucVo+w==",
|
||||
"requires": {
|
||||
"tslib": "2.3.0"
|
||||
},
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
"browserify": "^17.0.0",
|
||||
"clipboard": "^2.0.11",
|
||||
"domino": "^2.1.6",
|
||||
"echarts": "~5.6.0",
|
||||
"echarts": "~5.5.0",
|
||||
"ngx-echarts": "~17.2.0",
|
||||
"ngx-infinite-scroll": "^17.0.0",
|
||||
"qrcode": "1.5.1",
|
||||
|
||||
@@ -397,13 +397,13 @@
|
||||
</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">
|
||||
<p class="text-nowrap">—<span i18n="or">OR</span>—</p>
|
||||
</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">
|
||||
<p><ng-container i18n="transaction.pay|Pay button label">Pay</ng-container> <app-fiat [value]="cost"></app-fiat> with</p>
|
||||
@if (canPayWithCashapp) {
|
||||
@@ -421,13 +421,6 @@
|
||||
<img src="/resources/google-pay.png" height=37>
|
||||
</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>
|
||||
@@ -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>
|
||||
</div>
|
||||
</div>
|
||||
} @else if (step === 'cashapp' || step === 'applepay' || step === 'googlepay' || step === 'cardonfile') {
|
||||
} @else if (step === 'cashapp' || step === 'applepay' || step === 'googlepay') {
|
||||
<!-- Show checkout page -->
|
||||
<div class="row mb-md-1 text-center" id="confirm-title">
|
||||
<div class="col-sm" id="confirm-payment-title">
|
||||
@@ -466,7 +459,7 @@
|
||||
</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="col-sm">
|
||||
<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>
|
||||
} @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>
|
||||
} @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">
|
||||
<span i18n="accelerator.loading-payment-method">Loading payment method...</span>
|
||||
<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 { isDevMode } from '@angular/core';
|
||||
|
||||
export type PaymentMethod = 'balance' | 'bitcoin' | 'cashapp' | 'applePay' | 'googlePay' | 'cardOnFile';
|
||||
export type PaymentMethod = 'balance' | 'bitcoin' | 'cashapp' | 'applePay' | 'googlePay';
|
||||
|
||||
export type AccelerationEstimate = {
|
||||
hasAccess: boolean;
|
||||
@@ -26,7 +26,7 @@ export type AccelerationEstimate = {
|
||||
mempoolBaseFee: number;
|
||||
vsizeFee: 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;
|
||||
options: { // recommended bid options
|
||||
fee: number; // recommended userBid in sats
|
||||
@@ -49,7 +49,7 @@ export const MIN_BID_RATIO = 1;
|
||||
export const DEFAULT_BID_RATIO = 2;
|
||||
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({
|
||||
selector: 'app-accelerate-checkout',
|
||||
@@ -65,7 +65,6 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
|
||||
@Input() cashappEnabled: boolean = true;
|
||||
@Input() applePayEnabled: boolean = false;
|
||||
@Input() googlePayEnabled: boolean = true;
|
||||
@Input() cardOnFileEnabled: boolean = true;
|
||||
@Input() advancedEnabled: boolean = false;
|
||||
@Input() forceMobile: boolean = false;
|
||||
@Input() showDetails: boolean = false;
|
||||
@@ -118,7 +117,6 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
|
||||
loadingCashapp = false;
|
||||
loadingApplePay = false;
|
||||
loadingGooglePay = false;
|
||||
loadingCardOnFile = false;
|
||||
payments: any;
|
||||
cashAppPay: any;
|
||||
applePay: any;
|
||||
@@ -236,10 +234,6 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
|
||||
this.loadingGooglePay = true;
|
||||
this.setupSquare();
|
||||
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') {
|
||||
this.timePaid = Date.now();
|
||||
this.timeoutTimer = setTimeout(() => {
|
||||
@@ -460,8 +454,6 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
|
||||
await this.requestApplePayPayment();
|
||||
} else if (this._step === 'googlepay') {
|
||||
await this.requestGooglePayPayment();
|
||||
} else if (this._step === 'cardonfile') {
|
||||
this.loadingCardOnFile = false;
|
||||
}
|
||||
},
|
||||
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
|
||||
*/
|
||||
@@ -1066,22 +955,6 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
|
||||
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 {
|
||||
if (!this.hasAccessToBalanceMode) {
|
||||
return false;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<th class="time text-right" i18n="accelerator.requested">Requested</th>
|
||||
</ng-container>
|
||||
<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="pool text-right" i18n="mining.pool-name" *ngIf="!this.widget">Pool</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.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('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') && !acceleration.canceled" class="badge badge-danger"><ng-container i18n="accelerator.canceled">Failed</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>
|
||||
</td>
|
||||
<td class="date text-right" *ngIf="!this.widget">
|
||||
<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="d-inline align-middle">
|
||||
<span>To use the faucet, please </span>
|
||||
<a routerLink="/login" [queryParams]="{'redirectTo': '/testnet4/faucet'}">login</a>
|
||||
<span class="mr-2"> or</span>
|
||||
</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>
|
||||
}
|
||||
@else if (user && user.status === 'pending' && !user.email && user.snsId) {
|
||||
@@ -34,18 +36,18 @@
|
||||
</div>
|
||||
}
|
||||
@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="d-inline align-middle">
|
||||
<span class="mb-2 mr-2">To use the faucet, please</span>
|
||||
</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>
|
||||
}
|
||||
@else if (error === 'account_limited') {
|
||||
<div class="alert alert-mempool d-block text-center w-100">
|
||||
<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>
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
<tr>
|
||||
@for (branch of job.merkleBranches; track $index) {
|
||||
@if ($index === 0 && branch) {
|
||||
<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>
|
||||
}
|
||||
<td class="merkle" [style.background-color]="branch ? '#' + branch.slice(0, 6) : ''"></td>
|
||||
}
|
||||
@for (_ of [].constructor(Math.max(0, 12 - job.merkleBranches.length)); track $index) {
|
||||
<td class="merkle empty-branch"></td>
|
||||
|
||||
@@ -220,7 +220,6 @@ div.scrollable {
|
||||
|
||||
.merkle {
|
||||
width: 100px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.empty-branch {
|
||||
|
||||
@@ -361,10 +361,6 @@ export class PoolComponent implements OnInit {
|
||||
return block.height;
|
||||
}
|
||||
|
||||
reverseHash(hash: string) {
|
||||
return hash.match(/../g).reverse().join('');
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.slugSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
@@ -7,27 +7,30 @@
|
||||
<table *ngIf="poolsReady && (rows$ | async) as rows;" class="stratum-table">
|
||||
<thead>
|
||||
<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">
|
||||
Merkle Branches
|
||||
</td>
|
||||
<td class="pool">Pool</td>
|
||||
<td class="tag">Coinbase Tag</td>
|
||||
<td class="reward">Reward</td>
|
||||
<td class="height">Height</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@for (row of rows; track row.job.pool) {
|
||||
<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) {
|
||||
<td class="merkle" [style.background-color]="cell.hash ? '#' + cell.hash.slice(0, 6) : ''">
|
||||
@if ($index === 0 && cell.hash) {
|
||||
<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>
|
||||
}
|
||||
<div class="pipe-segment" [class]="pipeToClass(cell.type)"></div>
|
||||
</td>
|
||||
}
|
||||
<td class="pool">
|
||||
@@ -38,15 +41,6 @@
|
||||
</a>
|
||||
}
|
||||
</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>
|
||||
}
|
||||
</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 {
|
||||
|
||||
@@ -48,16 +48,14 @@ function parseTag(scriptSig: string): string {
|
||||
return (ascii.match(/\/.*\//)?.[0] || ascii).trim();
|
||||
}
|
||||
|
||||
function getMerkleBranchIds(merkleBranches: string[], numBranches: number, poolId: number): string[] {
|
||||
function getMerkleBranchIds(merkleBranches: string[], numBranches: number): string[] {
|
||||
let lastHash = '';
|
||||
const ids: string[] = [];
|
||||
for (let i = 0; i < numBranches; i++) {
|
||||
if (merkleBranches[i]) {
|
||||
lastHash = merkleBranches[i];
|
||||
ids.push(`${i}-${lastHash}`);
|
||||
} else {
|
||||
ids.push(`${i}-${lastHash}-${poolId}`);
|
||||
}
|
||||
ids.push(`${i}-${lastHash}`);
|
||||
}
|
||||
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 jobs: Record<string, TaggedStratumJob> = {};
|
||||
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) {
|
||||
return [];
|
||||
@@ -198,10 +196,6 @@ export class StratumList implements OnInit, OnDestroy {
|
||||
}[type];
|
||||
}
|
||||
|
||||
reverseHash(hash: string) {
|
||||
return hash.match(/../g).reverse().join('');
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.websocketService.stopTrackStratum();
|
||||
}
|
||||
|
||||
@@ -412,13 +412,13 @@ export interface Acceleration {
|
||||
feeDelta: number;
|
||||
blockHash: string;
|
||||
blockHeight: number;
|
||||
|
||||
acceleratedFeeRate?: number;
|
||||
boost?: number;
|
||||
bidBoost?: number;
|
||||
boostCost?: number;
|
||||
boostRate?: number;
|
||||
minedByPoolUniqueId?: number;
|
||||
canceled?: number;
|
||||
}
|
||||
|
||||
export interface AccelerationHistoryParams {
|
||||
|
||||
@@ -58,7 +58,6 @@ export class AuthServiceMempool {
|
||||
setAuth(auth: any) {
|
||||
if (!auth) {
|
||||
localStorage.removeItem('auth');
|
||||
localStorage.removeItem('authenticatorStatus');
|
||||
} else {
|
||||
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 });
|
||||
}
|
||||
|
||||
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[]> {
|
||||
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,
|
||||
faFastForward, faWallet, faUserClock, faWrench, faUserFriends, faQuestionCircle, faHistory, faSignOutAlt, faKey, faSuitcase, faIdCardAlt, faNetworkWired, faUserCheck,
|
||||
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 { MenuComponent } from '@components/menu/menu.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 { OnlyVsizeDirective, OnlyWeightDirective } from '@app/shared/components/weight-directives/weight-directives';
|
||||
import { GithubLogin } from '../components/github-login.component/github-login.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@@ -243,7 +242,6 @@ import { GithubLogin } from '../components/github-login.component/github-login.c
|
||||
TwitterWidgetComponent,
|
||||
FaucetComponent,
|
||||
TwitterLogin,
|
||||
GithubLogin,
|
||||
BitcoinInvoiceComponent,
|
||||
],
|
||||
imports: [
|
||||
@@ -378,7 +376,6 @@ import { GithubLogin } from '../components/github-login.component/github-login.c
|
||||
HttpErrorComponent,
|
||||
TwitterWidgetComponent,
|
||||
TwitterLogin,
|
||||
GithubLogin,
|
||||
BitcoinInvoiceComponent,
|
||||
BitcoinsatoshisPipe,
|
||||
|
||||
@@ -463,6 +460,5 @@ export class SharedModule {
|
||||
library.addIcons(faMoneyBillTrendUp);
|
||||
library.addIcons(faRobot);
|
||||
library.addIcons(faShareNodes);
|
||||
library.addIcons(faCreditCard);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,21 +3,11 @@
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- LOAD BEARING COMMENTS - DO NOT TOUCH! -->
|
||||
<!-- TITLE -->
|
||||
<title>mempool - Bitcoin Explorer</title>
|
||||
<!-- END TITLE -->
|
||||
|
||||
<!-- CONFIG -->
|
||||
<script src="/resources/config.js"></script>
|
||||
<!-- END CONFIG -->
|
||||
<!-- CUSTOMIZATION -->
|
||||
<script src="/resources/customize.js"></script>
|
||||
<!-- END CUSTOMIZATION -->
|
||||
<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 property="og:image" content="https://mempool.space/resources/previews/mempool-space-preview.jpg" />
|
||||
<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:image" content="https://mempool.space/resources/previews/mempool-space-preview.jpg" />
|
||||
<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="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="manifest" href="/resources/favicons/site.webmanifest">
|
||||
<link rel="shortcut icon" href="/resources/favicons/favicon.ico">
|
||||
<link id="canonical" rel="canonical" href="https://mempool.space">
|
||||
|
||||
<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/favicons/browserconfig.xml">
|
||||
<meta name="theme-color" content="#1d1f31">
|
||||
<!-- END FAVICONS -->
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</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