@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
JavaScript
;
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;