@substrate/api-sidecar
Version:
REST service that makes it easy to interact with blockchain nodes built using Substrate's FRAME framework.
201 lines • 8.53 kB
JavaScript
;
// Copyright 2017-2025 Parity Technologies (UK) Ltd.
// This file is part of Substrate API Sidecar.
//
// Substrate API Sidecar is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
// Introduced via `@polkadot/api v7.0.1`.
require("@polkadot/api-augment");
const express_1 = require("express");
const package_json_1 = __importDefault(require("../package.json"));
const apiRegistry_1 = require("./apiRegistry");
const App_1 = __importDefault(require("./App"));
const chains_config_1 = require("./chains-config");
const chains_config_2 = require("./chains-config");
const consoleOverride_1 = require("./logging/consoleOverride");
const Log_1 = require("./logging/Log");
const index_1 = require("./metrics/index");
const middleware = __importStar(require("./middleware"));
const parseArgs_1 = require("./parseArgs");
const SidecarConfig_1 = require("./SidecarConfig");
/*
* initApis function prepares the API registry and initializes the API
* it returns the specName of the main API to be used by sidecar
*/
async function initApis() {
const { config } = SidecarConfig_1.SidecarConfig;
const { logger } = Log_1.Log;
logger.info('Initializing APIs');
const requiredApis = [
{ url: config.SUBSTRATE.URL },
...config.SUBSTRATE.MULTI_CHAIN_URL.map((chain) => ({ url: chain.url, type: chain.type })),
].filter((apiOption) => !!apiOption.url);
// Create the API registry
const apis = await Promise.all(requiredApis.map((apiUrl) => apiRegistry_1.ApiPromiseRegistry.initApi(apiUrl.url, apiUrl.type || undefined)));
for (const api of apis) {
await api.isReady;
}
const specNames = [];
let isAssetHub = false;
let isAssetHubMigrated = false;
for (let i = 0; i < apis.length; i++) {
if (!apis[i]) {
logger.error('Failed to create API instance');
}
const api = apis[i];
// Gather some basic details about the node so we can display a nice message
const [chainName, { implName, specName }] = await Promise.all([
api.rpc.system.chain(),
api.rpc.state.getRuntimeVersion(),
]);
// This is assuming that the first requiredApi will be SUBSTRATE.URL
if (i === 0 && chains_config_2.assetHubSpecNames.has(specName.toString().toLowerCase())) {
isAssetHub = true;
let stage;
if (api.query.ahMigrator) {
stage = await api.query.ahMigrator.ahMigrationStage();
}
if (stage && stage.isMigrationDone) {
isAssetHubMigrated = true;
}
else if (api.query.staking && !api.query.ahMigrator) {
// Asset Hub is migrated, and removed the ahMigrator pallet.
isAssetHubMigrated = true;
}
apiRegistry_1.ApiPromiseRegistry.setAssetHubInfo({ isAssetHub, isAssetHubMigrated });
}
startUpPrompt(requiredApis[i].url, chainName.toString(), implName.toString());
specNames.push(specName.toString());
}
logger.info('All APIs initialized');
return specNames[0];
}
async function main() {
const { config } = SidecarConfig_1.SidecarConfig;
const { logger } = Log_1.Log;
// Overide console.{log, error, warn, etc}
(0, consoleOverride_1.consoleOverride)(logger);
logger.info(`Version: ${package_json_1.default.version}`);
const preMiddlewares = [(0, express_1.json)({ limit: config.EXPRESS.MAX_BODY }), middleware.httpLoggerCreate(logger)];
if (config.METRICS.ENABLED) {
// Create Metrics App
const metricsApp = new index_1.MetricsApp({
port: config.METRICS.PROM_PORT,
host: config.METRICS.PROM_HOST,
});
// Generate metrics middleware
preMiddlewares.push(metricsApp.preMiddleware());
// Start the Metrics server
metricsApp.listen();
}
const specNames = await initApis();
const pallets = [];
if (config.EXPRESS.INJECTED_CONTROLLERS) {
// Get the pallets from the API
const api = apiRegistry_1.ApiPromiseRegistry.getApi(specNames);
if (api) {
const chainPallets = api.registry.metadata.toJSON().pallets.map((p) => p.name);
pallets.push(...chainPallets);
}
}
const app = new App_1.default({
preMiddleware: preMiddlewares,
controllers: (0, chains_config_1.getControllers)(config, specNames, pallets),
postMiddleware: [
middleware.txError,
middleware.httpError,
middleware.error,
middleware.legacyError,
middleware.internalError,
],
port: config.EXPRESS.PORT,
host: config.EXPRESS.HOST,
});
// Start the server
const server = app.listen();
server.keepAliveTimeout = config.EXPRESS.KEEP_ALIVE_TIMEOUT;
server.headersTimeout = config.EXPRESS.KEEP_ALIVE_TIMEOUT + 5000;
}
/**
* Prompt the user with some basic info about the node and the network they have
* connected Sidecar to.
*
* @param url Url of the node Sidecar is connected to, can be a websocket or http address
* @param chainName chain name of the network Sidecar is connected to
* @param implName implementation name of the node Sidecar is connected to
*/
function startUpPrompt(url, chainName, implName) {
const { logger } = Log_1.Log;
logger.info(`Connected to chain ${chainName} on the ${implName} client at ${url}`);
// Split the Url to check for 2 things. Secure connection, and if its a local IP.
const splitUrl = url.split(':');
// If its 'ws' its not a secure connection.
const isSecure = splitUrl[0] === 'wss' || splitUrl[0] === 'https';
// Check if its a local IP.
const isLocal = splitUrl[1] === '//127.0.0.1' || splitUrl[1] === '//localhost';
if (!isSecure && !isLocal) {
logger.warn(`Using unencrypted connection to a public node (${url}); All traffic is sent over the internet in cleartext.`);
}
}
process.on('SIGINT', function () {
console.log('Caught interrupt signal, exiting...');
// TODO: disconnnect all APIs
process.exit(0);
});
const args = (0, parseArgs_1.parseArgs)();
if (args.version) {
console.log(`@substrate/api-sidecar v${package_json_1.default.version}`);
process.exit(0);
}
else {
main().catch(console.log);
}
//# sourceMappingURL=main.js.map