From ac0f56325b93e35f18e6ed7ce561071df5d7b32f Mon Sep 17 00:00:00 2001 From: Mononaut Date: Thu, 25 Apr 2024 20:18:37 +0000 Subject: [PATCH] Add customizable enterprise build --- frontend/angular.json | 1 + frontend/custom-sv-config.json | 32 +++++++++++++++++++ frontend/generate-config.js | 18 +++++++++++ .../src/app/services/enterprise.service.ts | 2 +- frontend/src/app/services/state.service.ts | 12 +++++++ frontend/src/app/services/theme.service.ts | 4 ++- frontend/src/index.mempool.html | 1 + 7 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 frontend/custom-sv-config.json diff --git a/frontend/angular.json b/frontend/angular.json index f55c09934..46cc3f667 100644 --- a/frontend/angular.json +++ b/frontend/angular.json @@ -166,6 +166,7 @@ "src/resources", "src/robots.txt", "src/config.js", + "src/customize.js", "src/config.template.js" ], "styles": [ diff --git a/frontend/custom-sv-config.json b/frontend/custom-sv-config.json new file mode 100644 index 000000000..69048946c --- /dev/null +++ b/frontend/custom-sv-config.json @@ -0,0 +1,32 @@ +{ + "theme": "contrast", + "enterprise": "elsalvador", + "dashboard": { + "widgets": [ + { + "component": "fees" + }, + { + "component": "balance", + "props": { + "address": "32ixEdVJWo3kmvJGMTZq5jAQVZZeuwnqzo" + } + }, + { + "component": "goggles" + }, + { + "component": "address", + "props": { + "address": "32ixEdVJWo3kmvJGMTZq5jAQVZZeuwnqzo" + } + }, + { + "component": "blocks" + }, + { + "component": "transactions" + } + ] + } +} \ No newline at end of file diff --git a/frontend/generate-config.js b/frontend/generate-config.js index c7a81a482..8f911dfe6 100644 --- a/frontend/generate-config.js +++ b/frontend/generate-config.js @@ -4,6 +4,7 @@ const { spawnSync } = require('child_process'); const CONFIG_FILE_NAME = 'mempool-frontend-config.json'; const GENERATED_CONFIG_FILE_NAME = 'src/resources/config.js'; const GENERATED_TEMPLATE_CONFIG_FILE_NAME = 'src/resources/config.template.js'; +const GENERATED_CUSTOMIZATION_FILE_NAME = 'src/resources/customize.js'; let settings = []; let configContent = {}; @@ -109,6 +110,23 @@ writeConfigTemplate(GENERATED_TEMPLATE_CONFIG_FILE_NAME, newConfigTemplate); const currentConfig = readConfig(GENERATED_CONFIG_FILE_NAME); +let customConfigJs = ''; +if (configContent && configContent.CUSTOMIZATION) { + const customConfig = readConfig(configContent.CUSTOMIZATION); + if (customConfig) { + console.log(`Customizing frontend using ${configContent.CUSTOMIZATION}`); + customConfigJs = `(function (window) { + window.__env = window.__env || {}; + window.__env.customize = ${customConfig}; + }((typeof global !== 'undefined') ? global : this)); + `; + } else { + throw new Error('Failed to load customization file'); + } +} + +writeConfig(GENERATED_CUSTOMIZATION_FILE_NAME, customConfigJs); + if (currentConfig && currentConfig === newConfig) { console.log(`No configuration updates, skipping ${GENERATED_CONFIG_FILE_NAME} file update`); return; diff --git a/frontend/src/app/services/enterprise.service.ts b/frontend/src/app/services/enterprise.service.ts index 4ad31bd9f..b1d042282 100644 --- a/frontend/src/app/services/enterprise.service.ts +++ b/frontend/src/app/services/enterprise.service.ts @@ -23,7 +23,7 @@ export class EnterpriseService { private stateService: StateService, private activatedRoute: ActivatedRoute, ) { - const subdomain = this.document.location.hostname.indexOf(this.exclusiveHostName) > -1 + const subdomain = this.stateService.env.customize.enterprise || this.document.location.hostname.indexOf(this.exclusiveHostName) > -1 && this.document.location.hostname.split(this.exclusiveHostName)[0] || false; if (subdomain && subdomain.match(/^[A-z0-9-_]+$/)) { this.subdomain = subdomain; diff --git a/frontend/src/app/services/state.service.ts b/frontend/src/app/services/state.service.ts index a083c538d..f9e49648c 100644 --- a/frontend/src/app/services/state.service.ts +++ b/frontend/src/app/services/state.service.ts @@ -20,6 +20,17 @@ export interface MarkBlockState { export interface ILoadingIndicators { [name: string]: number; } +export interface Customization { + theme: string; + enterprise?: string; + dashboard: { + widgets: { + component: string; + props: { [key: string]: any }; + }[]; + }; +} + export interface Env { TESTNET_ENABLED: boolean; SIGNET_ENABLED: boolean; @@ -50,6 +61,7 @@ export interface Env { ADDITIONAL_CURRENCIES: boolean; GIT_COMMIT_HASH_MEMPOOL_SPACE?: string; PACKAGE_JSON_VERSION_MEMPOOL_SPACE?: string; + customize?: Customization; } const defaultEnv: Env = { diff --git a/frontend/src/app/services/theme.service.ts b/frontend/src/app/services/theme.service.ts index 098089597..7981f37a3 100644 --- a/frontend/src/app/services/theme.service.ts +++ b/frontend/src/app/services/theme.service.ts @@ -2,6 +2,7 @@ import { Injectable } from '@angular/core'; import { Subject } from 'rxjs'; import { defaultMempoolFeeColors, contrastMempoolFeeColors } from '../app.constants'; import { StorageService } from './storage.service'; +import { StateService } from './state.service'; @Injectable({ providedIn: 'root' @@ -14,8 +15,9 @@ export class ThemeService { constructor( private storageService: StorageService, + private stateService: StateService, ) { - const theme = this.storageService.getValue('theme-preference') || 'default'; + const theme = this.storageService.getValue('theme-preference') || this.stateService.env.customize?.theme || 'default'; this.apply(theme); } diff --git a/frontend/src/index.mempool.html b/frontend/src/index.mempool.html index 838af21d0..ed5f7e0b4 100644 --- a/frontend/src/index.mempool.html +++ b/frontend/src/index.mempool.html @@ -5,6 +5,7 @@ mempool - Bitcoin Explorer +