UNPKG

@rsksmart/rif-storage-pinning

Version:

Application for providing your storage space to other to use in exchange of RIF Tokens

313 lines (312 loc) 12.5 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (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 (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.composeGc = exports.promptForFlag = exports.sleep = exports.runAndAwaitFirstEvent = exports.duplicateObject = exports.decodeByteArray = exports.buildHandler = exports.isValidEvent = exports.isEventWithProvider = exports.errorHandler = exports.bytesToMegabytes = exports.bnFloor = exports.BytesInMb = void 0; const web3_utils_1 = require("web3-utils"); const fs_1 = __importDefault(require("fs")); const bignumber_js_1 = __importDefault(require("bignumber.js")); const config_1 = __importDefault(require("config")); const path_1 = __importDefault(require("path")); const cli_ux_1 = __importDefault(require("cli-ux")); const command_1 = __importStar(require("@oclif/command")); const sequelize_store_1 = require("sequelize-store"); const sequelize_1 = require("./sequelize"); const store_1 = require("./store"); const providers_1 = require("./providers"); const jobs_manager_1 = require("./jobs-manager"); const ipfs_1 = require("./providers/ipfs"); const migrations_1 = require("./migrations"); const web3_events_1 = require("@rsksmart/web3-events"); exports.BytesInMb = Math.pow(1024, 2); // bytes function bnFloor(v) { return new bignumber_js_1.default(v).integerValue(bignumber_js_1.default.ROUND_FLOOR); } exports.bnFloor = bnFloor; function bytesToMegabytes(bytes) { const v = new bignumber_js_1.default(bytes); return v.div(exports.BytesInMb); } exports.bytesToMegabytes = bytesToMegabytes; function errorHandler(fn, logger) { return (...args) => { return fn(...args).catch(err => logger.error(err)); }; } exports.errorHandler = errorHandler; function isEventWithProvider(event) { return Boolean(event.returnValues.provider); } exports.isEventWithProvider = isEventWithProvider; function isValidEvent(value, handlers) { return value in handlers; } exports.isValidEvent = isValidEvent; function buildHandler(handlers, events) { return { events, process: (event, options) => { if (!isValidEvent(event.event, handlers)) { return Promise.reject(new Error(`Unknown event ${event.event}`)); } return handlers[event.event](event, options !== null && options !== void 0 ? options : {}); } }; } exports.buildHandler = buildHandler; /** * Utility function for decoding Solidity's byte32 array. * @param fileReference */ function decodeByteArray(fileReference) { return fileReference .map(web3_utils_1.hexToAscii) .join('') .trim() .replace(/\0/g, ''); // Remove null-characters } exports.decodeByteArray = decodeByteArray; /** * Duplicate object using JSON method. Functions are stripped. * @param obj */ function duplicateObject(obj) { return JSON.parse(JSON.stringify(obj)); } exports.duplicateObject = duplicateObject; /** * Create a Promise that is resolved when the specified event is emitted. * It is rejected if 'error' event is triggered. * * Be aware about the different mechanisms of EventEmitter and Promises! * Promise can be only ONCE fulfilled/rejected while EventEmitter can emit as many events * as it likes! Hence this utility resolves only upon first specified event or error. * * @param emitted * @param event */ function runAndAwaitFirstEvent(emitted, event, fn) { return new Promise((resolve, reject) => { emitted.on(event, resolve); emitted.on('error', reject); fn(); }); } exports.runAndAwaitFirstEvent = runAndAwaitFirstEvent; function sleep(ms, arg) { return new Promise(resolve => setTimeout(() => resolve(arg), ms)); } exports.sleep = sleep; /** * Prompt for flag if not provided wrapper * @param {IOptionFlag<any>} flag Oclif flag object * @return IOptionFlag<any> & { prompt: string | boolean } */ function promptForFlag(flag) { return Object.assign(Object.assign({}, flag), { prompt: true, required: false }); } exports.promptForFlag = promptForFlag; /** * Base class for Pinner service commands * It have predefined some basic flags and config processing * Also have some helpers for initializing DB, storing Offer ID and some ui (spinner, prompt for flag) * @abstract * @class BaseCommand */ class BaseCommand extends command_1.default { constructor(argv, config, options = {}) { super(argv, config); this.defaultInitOptions = { baseConfig: true, db: { migrate: false }, serviceRequired: true }; this.configuration = {}; this.initOptions = Object.assign(Object.assign({}, this.defaultInitOptions), options); } prompt(message, options = { required: true }) { return cli_ux_1.default.prompt(message, options); } confirm(message) { return cli_ux_1.default.confirm(message); } get spinner() { return cli_ux_1.default.action; } set offerId(offerId) { const store = sequelize_store_1.getObject(); store.offerId = offerId.toLowerCase(); } get offerId() { if (!this.sequelize) throw new Error('DB is not initialized'); const store = sequelize_store_1.getObject(); if (!store.offerId) throw new Error('Offer Id is not found in DB'); return sequelize_store_1.getObject().offerId; } getProviderManager() { return __awaiter(this, void 0, void 0, function* () { const jobsOptions = config_1.default.get('jobs'); const jobsManager = new jobs_manager_1.JobsManager(jobsOptions); const manager = new providers_1.ProviderManager(); manager.register(yield ipfs_1.IpfsProvider.bootstrap(jobsManager, duplicateObject(config_1.default.get('ipfs.clientOptions')))); return manager; }); } baseConfig(flags) { const configObject = { log: {} }; if (flags.log) { configObject.log.level = flags.log; } if (flags['log-filter']) { configObject.log.filter = flags['log-filter']; } if (flags['log-path']) { configObject.log.path = flags['log-path']; } let userConfig = {}; if (flags.config) { userConfig = config_1.default.util.parseFile(flags.config); } config_1.default.util.extendDeep(config_1.default, configObject); config_1.default.util.extendDeep(config_1.default, userConfig); this.configuration = { userConfig, configObject }; } resolveDbPath(db) { if (!db) return path_1.default.resolve(this.config.dataDir, config_1.default.get('db')); const parsed = path_1.default.parse(db); // File name if (!parsed.dir) { return path_1.default.resolve(this.config.dataDir, parsed.ext ? db : `${parsed.base}.sqlite`); } else { if (db[db.length - 1] === '/') throw new Error('Path should include the file name'); return path_1.default.resolve(`${db}${parsed.ext ? '' : '.sqlite'}`); } } promptForFlags(flagsSchema = {}, parsed) { return __awaiter(this, void 0, void 0, function* () { for (const [flagName, flagOption] of Object.entries(flagsSchema)) { // TODO extend to support prompt for boolean, options if (flagOption.prompt && !parsed.flags[flagName]) { parsed.flags[flagName] = yield this.prompt(typeof flagOption.prompt === 'string' ? flagOption.prompt : `Please enter ${flagName}`); } } return parsed; }); } initDB(path, options) { return __awaiter(this, void 0, void 0, function* () { const sequelize = yield sequelize_1.sequelizeFactory(path); const migrator = new migrations_1.Migration(sequelize); // Init store yield store_1.initStore(sequelize); // Run migration if (options === null || options === void 0 ? void 0 : options.migrate) { if ((yield migrator.pending()).length) { if ((options === null || options === void 0 ? void 0 : options.skipPrompt) || (yield this.prompt('DB Migration required! Run Migration (y/n)?'))) { yield migrator.up(); } else { this.exit(); } } } yield web3_events_1.Web3Events.init(sequelize); // And Web3Events models to Sequelize this.sequelize = sequelize; return sequelize; }); } // TODO: Rework this with correct types, supporting the actual Flags parseWithPrompt(command) { return this.promptForFlags(command.flags, this.parse(command)); } init() { return __awaiter(this, void 0, void 0, function* () { const { db, baseConfig, serviceRequired } = this.initOptions; this.parsedArgs = yield this.parseWithPrompt(this.constructor); if (baseConfig) this.baseConfig(this.parsedArgs.flags); this.dbPath = this.resolveDbPath(this.parsedArgs.flags.db); if (serviceRequired && !fs_1.default.existsSync(this.dbPath)) { throw new Error('Service was not yet initialized, first run \'init\' command!'); } if (db) { yield this.initDB(this.dbPath, Object.assign(Object.assign({}, db), { skipPrompt: Boolean(this.parsedArgs.flags.skipPrompt) })); } }); } } exports.default = BaseCommand; BaseCommand.flags = { db: command_1.flags.string({ char: 'd', description: 'Name or path to DB file', env: 'RIFS_DB', required: false }), config: command_1.flags.string({ description: 'path to JSON config file to load', env: 'RIFS_CONFIG' }), log: command_1.flags.string({ description: 'what level of information to log', options: ['error', 'warn', 'info', 'verbose', 'debug'], default: 'info', env: 'LOG_LEVEL' }), skipPrompt: command_1.flags.boolean({ description: 'Answer yes for any prompting', default: false }), 'log-filter': command_1.flags.string({ description: 'what components should be logged (+-, chars allowed)' }), 'log-path': command_1.flags.string({ description: 'log to file, default is STDOUT' }) }; function composeGc(fns) { return function (blockHeader) { return __awaiter(this, void 0, void 0, function* () { for (const gc of fns) { yield gc(blockHeader); } }); }; } exports.composeGc = composeGc;