diff --git a/frontend/src/index.mempool.html b/frontend/src/index.mempool.html index ed5f7e0b4..14dc8dcc5 100644 --- a/frontend/src/index.mempool.html +++ b/frontend/src/index.mempool.html @@ -3,11 +3,21 @@ + + + mempool - Bitcoin Explorer + + + + + + + @@ -21,19 +31,21 @@ + + - + diff --git a/production/mempool-build-custom.js b/production/mempool-build-custom.js new file mode 100644 index 000000000..c7e3def89 --- /dev/null +++ b/production/mempool-build-custom.js @@ -0,0 +1,128 @@ +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, `${config.meta.title}`); + + // substitute customization script + newhtml = newhtml.replace(/\<\!\-\- CUSTOMIZATION \-\-\>.*\<\!\-\- END CUSTOMIZATION \-\-\>/gis, ``); + + // substitute meta tags + newhtml = newhtml.replace(/\<\!\-\- META \-\-\>.*\<\!\-\- END META \-\-\>/gis, ` + + + + + + + + + + + + `); + + + // substitute favicons + newhtml = newhtml.replace(/\<\!\-\- FAVICONS -->.*\<\!\-\- END FAVICONS \-\-\>/gis, ` + + + + + + + + + + `); + + 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/` + 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();