diff --git a/README.md b/README.md index f39a907..5642550 100644 --- a/README.md +++ b/README.md @@ -37,8 +37,8 @@ The below instructions are geared toward BTC, but can be adapted easily to other 1. Clone this repo 2. `npm install` to install all required dependencies -3. Edit the "rpc" settings in [env.js](app/env.js) to target your node -4. Optional: Change the "coin" value in [env.js](app/env.js). Currently supported values are "BTC" and "LTC". +3. Edit the "rpc" settings in [credentials.js](app/credentials.js) to target your node +4. Optional: Change the "coin" value in [config.js](app/config.js). Currently supported values are "BTC" and "LTC". 5. `npm start` to start the local server 6. Visit http://127.0.0.1:3002/ diff --git a/app.js b/app.js index cd581a1..4b164d1 100755 --- a/app.js +++ b/app.js @@ -9,7 +9,7 @@ var logger = require('morgan'); var cookieParser = require('cookie-parser'); var bodyParser = require('body-parser'); var session = require("express-session"); -var env = require("./app/env.js"); +var config = require("./app/config.js"); var simpleGit = require('simple-git'); var utils = require("./app/utils.js"); var moment = require("moment"); @@ -45,7 +45,7 @@ app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false })); app.use(cookieParser()); app.use(session({ - secret: env.cookiePassword, + secret: config.cookiePassword, resave: false, saveUninitialized: false })); @@ -53,17 +53,17 @@ app.use(express.static(path.join(__dirname, 'public'))); function refreshExchangeRate() { - if (coins[env.coin].exchangeRateData) { - request(coins[env.coin].exchangeRateData.jsonUrl, function(error, response, body) { + if (coins[config.coin].exchangeRateData) { + request(coins[config.coin].exchangeRateData.jsonUrl, function(error, response, body) { if (!error && response && response.statusCode && response.statusCode == 200) { var responseBody = JSON.parse(body); - var exchangeRate = coins[env.coin].exchangeRateData.responseBodySelectorFunction(responseBody); + var exchangeRate = coins[config.coin].exchangeRateData.responseBodySelectorFunction(responseBody); if (exchangeRate > 0) { global.exchangeRate = exchangeRate; global.exchangeRateUpdateTime = new Date(); - console.log("Using exchange rate: " + global.exchangeRate + " USD/" + coins[env.coin].name + " starting at " + global.exchangeRateUpdateTime); + console.log("Using exchange rate: " + global.exchangeRate + " USD/" + coins[config.coin].name + " starting at " + global.exchangeRateUpdateTime); } else { console.log("Unable to get exchange rate data"); @@ -81,34 +81,34 @@ function refreshExchangeRate() { app.runOnStartup = function() { - global.env = env; - global.coinConfig = coins[env.coin]; + global.config = config; + global.coinConfig = coins[config.coin]; global.coinConfigs = coins; console.log("Running RPC Explorer for coin: " + global.coinConfig.name); - if (env.rpc) { - console.log("Connecting via RPC to node at " + env.rpc.host + ":" + env.rpc.port); + if (config.credentials.rpc) { + console.log("Connecting via RPC to node at " + config.credentials.rpc.host + ":" + config.credentials.rpc.port); global.client = new bitcoinCore({ - host: env.rpc.host, - port: env.rpc.port, - username: env.rpc.username, - password: env.rpc.password, + host: config.credentials.rpc.host, + port: config.credentials.rpc.port, + username: config.credentials.rpc.username, + password: config.credentials.rpc.password, timeout: 5000 }); } - if (env.donationAddresses) { + if (config.donationAddresses) { var getDonationAddressQrCode = function(coinId) { - qrcode.toDataURL(env.donationAddresses[coinId].address, function(err, url) { + qrcode.toDataURL(config.donationAddresses[coinId].address, function(err, url) { global.donationAddressQrCodeUrls[coinId] = url; }); }; global.donationAddressQrCodeUrls = {}; - env.donationAddresses.coins.forEach(function(item) { + config.donationAddresses.coins.forEach(function(item) { getDonationAddressQrCode(item); }); } @@ -145,15 +145,14 @@ app.runOnStartup = function() { app.use(function(req, res, next) { // make session available in templates res.locals.session = req.session; - res.locals.debug = env.debug; - if (env.rpc && req.session.host == null) { - req.session.host = env.rpc.host; - req.session.port = env.rpc.port; - req.session.username = env.rpc.username; + if (config.credentials.rpc && req.session.host == null) { + req.session.host = config.credentials.rpc.host; + req.session.port = config.credentials.rpc.port; + req.session.username = config.credentials.rpc.username; } - res.locals.env = global.env; + res.locals.config = global.config; res.locals.coinConfig = global.coinConfig; res.locals.host = req.session.host; diff --git a/app/env.js b/app/config.js similarity index 60% rename from app/env.js rename to app/config.js index 082a769..51fccec 100644 --- a/app/env.js +++ b/app/config.js @@ -1,3 +1,5 @@ +var credentials = require("./credentials.js"); + module.exports = { cookiePassword: "0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f", demoSite: true, @@ -24,15 +26,7 @@ module.exports = { "walletpassphrasechange" ], - // Edit "rpc" below to target your node. - // You may delete this section if you wish to connect manually via the UI. - - rpc: { - host:"127.0.0.1", - port:8332, - username:"rpc-username", - password:"rpc-password" - }, + credentials: credentials, // Edit "ipWhitelistForRpcCommands" regex to limit access to RPC Browser / Terminal to matching IPs ipWhitelistForRpcCommands:/^(127\.0\.0\.1)?(\:\:1)?$/, @@ -40,10 +34,21 @@ module.exports = { googleAnalyticsTrackingId:"", sentryUrl:"", + miningPoolsConfigUrl:"https://raw.githubusercontent.com/blockchain/Blockchain-Known-Pools/master/pools.json", + donationAddresses:{ coins:["BTC", "LTC"], - + "BTC":{address:"3NPGpNyLLmVKCEcuipBs7G4KpQJoJXjDGe", urlPrefix:"bitcoin:"}, "LTC":{address:"ME4pXiXuWfEi1ANBDo9irUJVcZBhsTx14i", urlPrefix:"litecoin:"} }, -}; \ No newline at end of file + + headerDropdownLinks: { + title:"Related Tools", + links:[ + {name: "Bitcoin Explorer", url:"https://btc-explorer.chaintools.io", imgUrl:"/img/logo/btc.svg"}, + {name: "Litecoin Explorer", url:"https://ltc-explorer.chaintools.io", imgUrl:"/img/logo/ltc.svg"}, + {name: "Lightning Explorer", url:"https://lightning-explorer.chaintools.io", imgUrl:"/img/logo/lightning.svg"}, + ] + } +}; diff --git a/app/credentials.js b/app/credentials.js new file mode 100644 index 0000000..edf9ef5 --- /dev/null +++ b/app/credentials.js @@ -0,0 +1,12 @@ +module.exports = { + + // Edit "rpc" below to target your node. + // You may delete this section if you wish to connect manually via the UI. + + rpc: { + host:"localhost", + port:8332, + username:"username", + password:"password" + } +}; diff --git a/app/rpcApi.js b/app/rpcApi.js index 2a6566f..2af9de4 100644 --- a/app/rpcApi.js +++ b/app/rpcApi.js @@ -1,16 +1,16 @@ var utils = require("./utils.js"); -var env = require("./env.js"); +var config = require("./config.js"); var coins = require("./coins.js"); function getGenesisBlockHash() { - return coins[env.coin].genesisBlockHash; + return coins[config.coin].genesisBlockHash; } function getGenesisCoinbaseTransactionId() { - return coins[env.coin].genesisCoinbaseTransactionId; + return coins[config.coin].genesisCoinbaseTransactionId; } function getRpcData(cmd) { @@ -92,7 +92,7 @@ function getMempoolStats() { } } - var satoshiPerByteBucketMaxima = coins[env.coin].feeSatoshiPerByteBucketMaxima; + var satoshiPerByteBucketMaxima = coins[config.coin].feeSatoshiPerByteBucketMaxima; var bucketCount = satoshiPerByteBucketMaxima.length + 1; var satoshiPerByteBuckets = []; @@ -263,9 +263,9 @@ function getTransactionInputs(transaction, inputLimit=0) { function getRawTransaction(txid) { return new Promise(function(resolve, reject) { - if (txid == coins[env.coin].genesisCoinbaseTransactionId) { + if (txid == coins[config.coin].genesisCoinbaseTransactionId) { getBlockByHeight(0).then(function(blockZeroResult) { - var result = coins[env.coin].genesisCoinbaseTransaction; + var result = coins[config.coin].genesisCoinbaseTransaction; result.confirmations = blockZeroResult.getblock.confirmations; resolve(result); @@ -302,11 +302,11 @@ function getRawTransactions(txids) { return; } - if (coins[env.coin].genesisCoinbaseTransactionId) { - if (txids.length == 1 && txids[0] == coins[env.coin].genesisCoinbaseTransactionId) { + if (coins[config.coin].genesisCoinbaseTransactionId) { + if (txids.length == 1 && txids[0] == coins[config.coin].genesisCoinbaseTransactionId) { // copy the "confirmations" field from genesis block to the genesis-coinbase tx getBlockByHeight(0).then(function(blockZeroResult) { - var result = coins[env.coin].genesisCoinbaseTransaction; + var result = coins[config.coin].genesisCoinbaseTransaction; result.confirmations = blockZeroResult.getblock.confirmations; resolve([result]); @@ -558,7 +558,7 @@ function getRpcMethodHelp(methodName) { } function getHistoricalData() { - var sortedList = coins[env.coin].historicalData; + var sortedList = coins[config.coin].historicalData; sortedList.sort(function(a, b){ return ((a.date > b.date) ? 1 : -1); }); diff --git a/app/utils.js b/app/utils.js index ccd0f1d..a3ddbfa 100644 --- a/app/utils.js +++ b/app/utils.js @@ -1,5 +1,5 @@ var Decimal = require("decimal.js"); -var env = require("./env.js"); +var config = require("./config.js"); var coins = require("./coins.js"); function doSmartRedirect(req, res, defaultUrl) { @@ -103,8 +103,8 @@ function formatCurrencyAmount(amount, formatType) { return addThousandsSeparators(dec.toDecimalPlaces(decimalPlaces)) + " " + formatCurrencyCache[formatType].name; } - for (var x = 0; x < coins[env.coin].currencyUnits.length; x++) { - var currencyUnit = coins[env.coin].currencyUnits[x]; + for (var x = 0; x < coins[config.coin].currencyUnits.length; x++) { + var currencyUnit = coins[config.coin].currencyUnits[x]; for (var y = 0; y < currencyUnit.values.length; y++) { var currencyUnitValue = currencyUnit.values[y]; @@ -129,7 +129,7 @@ function formatCurrencyAmount(amount, formatType) { } function formatCurrencyAmountInSmallestUnits(amount) { - return formatCurrencyAmount(amount, coins[env.coin].currencyUnits[coins[env.coin].currencyUnits.length - 1].name); + return formatCurrencyAmount(amount, coins[config.coin].currencyUnits[coins[config.coin].currencyUnits.length - 1].name); } // ref: https://stackoverflow.com/a/2901298/673828 @@ -145,7 +145,7 @@ function formatExchangedCurrency(amount) { var dec = new Decimal(amount); dec = dec.times(global.exchangeRate); - return addThousandsSeparators(dec.toDecimalPlaces(2)) + " " + coins[env.coin].exchangeRateData.exchangedCurrencyName; + return addThousandsSeparators(dec.toDecimalPlaces(2)) + " " + coins[config.coin].exchangeRateData.exchangedCurrencyName; } return ""; diff --git a/routes/baseActionsRouter.js b/routes/baseActionsRouter.js index 520450e..78731d6 100644 --- a/routes/baseActionsRouter.js +++ b/routes/baseActionsRouter.js @@ -3,7 +3,7 @@ var router = express.Router(); var util = require('util'); var moment = require('moment'); var utils = require('./../app/utils'); -var env = require("./../app/env"); +var config = require("./../app/config.js"); var bitcoinCore = require("bitcoin-core"); var rpcApi = require("./../app/rpcApi"); var qrcode = require('qrcode'); @@ -104,7 +104,7 @@ router.get("/mempool-summary", function(req, res) { res.render("mempool-summary"); }); }).catch(function(err) { - res.locals.userMessage = "Unable to connect to Bitcoin Node at " + env.rpc.host + ":" + env.rpc.port; + res.locals.userMessage = "Error: " + err; res.render("mempool-summary"); }); @@ -214,7 +214,7 @@ router.get("/blocks", function(req, res) { res.render("blocks"); }); }).catch(function(err) { - res.locals.userMessage = "Unable to connect to Bitcoin Node at " + env.rpc.host + ":" + env.rpc.port; + res.locals.userMessage = "Error: " + err; res.render("blocks"); }); @@ -467,12 +467,12 @@ router.get("/address/:address", function(req, res) { }); router.get("/rpc-terminal", function(req, res) { - if (!env.demoSite) { + if (!config.demoSite) { var ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress; - var match = env.ipWhitelistForRpcCommands.exec(ip); + var match = config.ipWhitelistForRpcCommands.exec(ip); if (!match) { - res.send("RPC Terminal / Browser may not be accessed from '" + ip + "'. This restriction can be modified in your env.js file."); + res.send("RPC Terminal / Browser may not be accessed from '" + ip + "'. This restriction can be modified in your config.js file."); return; } @@ -482,12 +482,12 @@ router.get("/rpc-terminal", function(req, res) { }); router.post("/rpc-terminal", function(req, res) { - if (!env.demoSite) { + if (!config.demoSite) { var ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress; - var match = env.ipWhitelistForRpcCommands.exec(ip); + var match = config.ipWhitelistForRpcCommands.exec(ip); if (!match) { - res.send("RPC Terminal / Browser may not be accessed from '" + ip + "'. This restriction can be modified in your env.js file."); + res.send("RPC Terminal / Browser may not be accessed from '" + ip + "'. This restriction can be modified in your config.js file."); return; } @@ -506,8 +506,8 @@ router.post("/rpc-terminal", function(req, res) { } }); - if (env.rpcBlacklist.includes(cmd.toLowerCase())) { - res.write("Sorry, that RPC command is blacklisted. If this is your server, you may allow this command by removing it from the 'rpcBlacklist' setting in env.js.", function() { + if (config.rpcBlacklist.includes(cmd.toLowerCase())) { + res.write("Sorry, that RPC command is blacklisted. If this is your server, you may allow this command by removing it from the 'rpcBlacklist' setting in config.js.", function() { res.end(); }); @@ -540,12 +540,12 @@ router.post("/rpc-terminal", function(req, res) { }); router.get("/rpc-browser", function(req, res) { - if (!env.demoSite) { + if (!config.demoSite) { var ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress; - var match = env.ipWhitelistForRpcCommands.exec(ip); + var match = config.ipWhitelistForRpcCommands.exec(ip); if (!match) { - res.send("RPC Terminal / Browser may not be accessed from '" + ip + "'. This restriction can be modified in your env.js file."); + res.send("RPC Terminal / Browser may not be accessed from '" + ip + "'. This restriction can be modified in your config.js file."); return; } @@ -599,8 +599,8 @@ router.get("/rpc-browser", function(req, res) { res.locals.argValues = argValues; - if (env.rpcBlacklist.includes(req.query.method.toLowerCase())) { - res.locals.methodResult = "Sorry, that RPC command is blacklisted. If this is your server, you may allow this command by removing it from the 'rpcBlacklist' setting in env.js."; + if (config.rpcBlacklist.includes(req.query.method.toLowerCase())) { + res.locals.methodResult = "Sorry, that RPC command is blacklisted. If this is your server, you may allow this command by removing it from the 'rpcBlacklist' setting in config.js."; res.render("browser"); diff --git a/views/about.pug b/views/about.pug index b745a26..5f3d98a 100644 --- a/views/about.pug +++ b/views/about.pug @@ -14,21 +14,21 @@ block content p Pull requests are welcome! a(href="https://github.com/janoside/btc-rpc-explorer") github.com/janoside/btc-rpc-explorer - if (env.donationAddresses) + if (config.donationAddresses) hr p If you value this tool and want to support my work on this project, please consider a small donation. Anything is appreciated! - each coin, index in env.donationAddresses.coins + each coin, index in config.donationAddresses.coins div(style="display: inline-block;", class="text-md-center mb-3", class=(index > 0 ? "ml-md-5" : false)) - if (env.donationAddresses[coin].urlPrefix) - a(href=("bitcoin:" + env.donationAddresses["BTC"])) #{coinConfigs[coin].name} (#{coin}) + if (config.donationAddresses[coin].urlPrefix) + a(href=("bitcoin:" + config.donationAddresses["BTC"])) #{coinConfigs[coin].name} (#{coin}) else span #{coinConfigs[coin].name} (#{coin}) br - span #{env.donationAddresses[coin].address} + span #{config.donationAddresses[coin].address} br - img(src=donationAddressQrCodeUrls[coin], alt=env.donationAddresses[coin].address, style="border: solid 1px #ccc;") + img(src=donationAddressQrCodeUrls[coin], alt=config.donationAddresses[coin].address, style="border: solid 1px #ccc;") br \ No newline at end of file diff --git a/views/index.pug b/views/index.pug index 9933857..4499285 100644 --- a/views/index.pug +++ b/views/index.pug @@ -7,7 +7,7 @@ block content h1(class="h2") #{coinConfig.siteTitle} hr - if (env.demoSite) + if (config.demoSite) div(class="alert alert-primary", role="alert") p strong #{coinConfig.siteTitle} diff --git a/views/layout.pug b/views/layout.pug index 25fef52..be6a542 100644 --- a/views/layout.pug +++ b/views/layout.pug @@ -10,7 +10,7 @@ html link(rel="stylesheet", href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/default.min.css") link(rel='stylesheet', href='/css/styling.css') - link(rel="icon", type="image/png", href=("/img/logo/" + env.coin.toLowerCase() + ".png")) + link(rel="icon", type="image/png", href=("/img/logo/" + config.coin.toLowerCase() + ".png")) block headContent title Explorer @@ -34,12 +34,12 @@ html a(href="/about", class="nav-link") span About - if (env.headerDropdownLinks) + if (config.headerDropdownLinks) li(class="nav-item dropdown") a(class="nav-link dropdown-toggle", href="javascript:void(0)", id="navbarDropdown", role="button", data-toggle="dropdown", aria-haspopup="true", aria-expanded="false") - span #{env.headerDropdownLinks.title} + span #{config.headerDropdownLinks.title} div(class="dropdown-menu", aria-labelledby="navbarDropdown") - each item in env.headerDropdownLinks.links + each item in config.headerDropdownLinks.links a(class="dropdown-item", href=item.url) img(src=item.imgUrl, style="width: 24px; height: 24px; margin-right: 8px;") span #{item.name} @@ -104,7 +104,7 @@ html a(href=("https://github.com/janoside/btc-rpc-explorer/commit/" + sourcecodeVersion)) #{sourcecodeVersion} span(style="color: #ccc;") (#{sourcecodeDate}) - if (env.demoSite) + if (config.demoSite) dt Public Demos dd if (coinConfig.demoSiteUrl) @@ -126,13 +126,13 @@ html dt Support Development of #{coinConfig.siteTitle} dd div(class="text-md-right text-center") - each coin, index in env.donationAddresses.coins + each coin, index in config.donationAddresses.coins div(style="display: inline-block;", class="text-center mb-3", class=(index > 0 ? "ml-md-3" : false)) - img(src=donationAddressQrCodeUrls[coin], alt=env.donationAddresses[coin].address, style="border: solid 1px #ccc;") + img(src=donationAddressQrCodeUrls[coin], alt=config.donationAddresses[coin].address, style="border: solid 1px #ccc;") br - if (env.donationAddresses[coin].urlPrefix) + if (config.donationAddresses[coin].urlPrefix) span #{coin}: - a(href=(env.donationAddresses[coin].urlPrefix + env.donationAddresses[coin].address)) #{env.donationAddresses[coin].address.substring(0, 10)}... + a(href=(config.donationAddresses[coin].urlPrefix + config.donationAddresses[coin].address)) #{config.donationAddresses[coin].address.substring(0, 10)}... else span #{coin} @@ -152,19 +152,19 @@ html hljs.initHighlightingOnLoad(); - if (env.sentryUrl && env.sentryUrl.length > 0) + if (config.sentryUrl && config.sentryUrl.length > 0) script(src="https://cdn.ravenjs.com/3.25.2/raven.min.js", crossorigin="anonymous") script. - Raven.config("#{env.sentryUrl}").install(); + Raven.config("#{config.sentryUrl}").install(); - if (env.googleAnalyticsTrackingId && env.googleAnalyticsTrackingId.length > 0) - script(async, src=("https://www.googletagmanager.com/gtag/js?id=#{env.googleAnalyticsTrackingId}")) + if (config.googleAnalyticsTrackingId && config.googleAnalyticsTrackingId.length > 0) + script(async, src=("https://www.googletagmanager.com/gtag/js?id=#{config.googleAnalyticsTrackingId}")) script. window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); - gtag('config', '#{env.googleAnalyticsTrackingId}'); + gtag('config', '#{config.googleAnalyticsTrackingId}'); block endOfBody diff --git a/views/terminal.pug b/views/terminal.pug index fdafe9a..fc447f4 100644 --- a/views/terminal.pug +++ b/views/terminal.pug @@ -6,7 +6,7 @@ block content h1(class="h2") RPC Terminal div(class="col") - if (!env.demoSite && (!env.rpc || !env.rpc.rpc)) + if (!config.demoSite && (!config.credentials.rpc || !config.credentials.rpc.rpc)) span(style="float: right;") a(href="/disconnect", class="btn btn-secondary") Disconnect from node