Files
sashimono/examples/message-board/message-board.js
2021-07-29 14:11:53 +05:30

405 lines
19 KiB
JavaScript

const https = require('https');
const http = require('http');
const fs = require('fs');
const readLine = require('readline');
const { v4: uuidv4 } = require('uuid');
const { execSync } = require("child_process");
const express = require('express');
const cliDevPath = "../../build/sashi";
const cliProdPath = "/usr/bin/sashi";
let cliPath;
let args = process.argv;
if (args.length == 3 && args[2] == 'prod')
cliPath = cliProdPath;
else if (args.length == 2 || (args.length == 3 && args[2] == 'dev'))
cliPath = cliDevPath;
else {
console.log("Arguments mismatch.\n Usage: node message-board (optional)<dev|prod>");
process.exit(0);
}
if (!fs.existsSync(cliPath)) {
console.error(`Sashi CLI does not exist in ${cliPath}.`)
process.exit(0);
}
let restServer;
/**
* Interactive interface to get message from the command line and sent it to all the connected agents.
*/
const interatctiveInterface = async () => {
// start listening for stdin
const rl = readLine.createInterface({
input: process.stdin,
output: process.stdout
});
// On ctrl + c we should close SA connection gracefully.
rl.on('SIGINT', () => {
console.log('SIGINT received...');
rl.close();
restServer && restServer.close();
});
const askForInput = (label, defaultValue) => {
return new Promise(resolve => {
rl.question(`${label}? `, (input) => {
resolve(input && input.length > 0 ? input : defaultValue);
})
})
}
console.log("Ready to accept inputs.");
const inputPump = () => {
rl.question('', async (inp) => {
if (inp.length > 0) {
switch (inp) {
case 'status':
checkAgentStatus();
break;
case 'create':
contractId = await askForInput('Contract ID (default:uuidv4)', uuidv4());
image = await askForInput('Image: 1=ubuntu(default) | 2=nodejs', "1");
if (image != "1" && image != "2") {
console.error('Invalid image. (Should be "1" or "2").')
break;
}
sendToAgent(JSON.stringify({
type: 'create',
owner_pubkey: 'ed5cb83404120ac759609819591ef839b7d222c84f1f08b3012f490586159d2b50',
contract_id: contractId,
image: (image == "1" ? "hp.0.5-ubt.20.04" : "hp.0.5-ubt.20.04-njs.14")
}));
break;
case 'initiate':
containerName = await askForInput('Container Name');
let config = {};
modifyNode = await askForInput('Modify node section? [y/N]', 'n');
if (modifyNode === 'y' || modifyNode === 'Y') {
role = await askForInput('Role: validator | observer(optional)');
if (role && role != 'validator' && role != 'observer') {
console.error('Invalid role. (Should be "validator" or "observer").')
break;
}
history = await askForInput('History <{full|custom},max_primary_shards,max_raw_shards> (custom,1,1)', "custom,1,1");
split = [];
if (history) {
split = history.split(',');
if (split.length == 0 || split.length !== 3) {
console.error('Invalid history.')
break;
}
else if (split[0] != 'full' && split[0] != 'custom') {
console.error('Invalid history. (Should be "full" or "custom").')
break;
}
}
config.node = {
role: role,
history: history ? split[0] : undefined,
history_config: history ? {
max_primary_shards: parseInt(split[1]),
max_raw_shards: parseInt(split[2])
} : undefined
};
}
modifyContract = await askForInput('Modify contract section? [y/N]', 'n');
if (modifyContract === 'y' || modifyContract === 'Y') {
unl = await askForInput('Comma seperated UNL <pubkey1>,<pubkey2>,...');
execute = await askForInput('Execute contract? (optional)');
log = await askForInput('log <{true|false},max_mbytes_per_file,max_file_count> (optional)');
if (log) {
split = log.split(',');
if (split.length == 0 || split.length !== 3) {
console.error('Invalid log config.')
break;
}
else if (split[0] != 'true' && split[0] != 'false') {
console.error('Log enable tag should be either true or false')
break;
}
}
config.contract = {
execute: execute ? (execute === 'true' ? true : false) : undefined,
log: log ? {
enable: split[0] === 'true' ? true : false,
max_mbytes_per_file: parseInt(split[1]),
max_file_count: parseInt(split[2])
} : undefined,
unl: unl ? unl.split(',') : undefined
}
}
modifyMesh = await askForInput('Modify mesh section? [y/N]', 'n');
if (modifyMesh === 'y' || modifyMesh === 'Y') {
idleTimeout = await askForInput('Idle timeout?(optional)');
peers = await askForInput('Comma seperated Peer List <host1:port1>,<host2:port2>,...(optional)');
msgForwarding = await askForInput('Message forwarding [true|false]?(optional)');
set01 = await askForInput('Comma seperated max_connections, max_known_connections and max_in_connections_per_host?(optional)');
if (set01) {
split01 = set01.split(',');
if (split01.length == 0 || split01.length !== 3) {
console.error('Make sure to add all three. Eg: 1,1,1');
break;
}
}
set02 = await askForInput('Comma seperated max_bytes_per_msg, max_bytes_per_min and max_bad_msgs_per_min?(optional)');
if (set02) {
split02 = set02.split(',');
if (split02.length == 0 || split02.length !== 3) {
console.error('Make sure to add all three. Eg: 1,1,1');
break;
}
}
set03 = await askForInput('Comma seperated max_bad_msgsigs_per_min and max_dup_msgs_per_min?(optional)');
if (set03) {
split03 = set03.split(',');
if (split03.length == 0 || split03.length !== 2) {
console.error('Make sure to add all two. Eg: 1,1');
break;
}
}
peerDiscovery = await askForInput('Peer discovery <{true|false}, Interval>?(optional)');
if (peerDiscovery) {
peerDiscovery = peerDiscovery.split(',');
if (peerDiscovery.length == 0 || peerDiscovery.length !== 2) {
console.error('Make sure to add all two. Eg: true,10000');
break;
}
}
config.mesh = {
idle_timeout: idleTimeout ? parseInt(idleTimeout) : undefined,
known_peers: peers ? peers.split(',') : undefined,
msg_forwarding: msgForwarding ? (msgForwarding === 'true' ? true : false) : undefined,
max_connections: set01 ? parseInt(split01[0]) : undefined,
max_known_connections: set01 ? parseInt(split01[1]) : undefined,
max_in_connections_per_host: set01 ? parseInt(split01[2]) : undefined,
max_bytes_per_msg: set02 ? parseInt(split02[0]) : undefined,
max_bytes_per_min: set02 ? parseInt(split02[1]) : undefined,
max_bad_msgs_per_min: set02 ? parseInt(split02[2]) : undefined,
max_bad_msgsigs_per_min: set03 ? parseInt(split03[0]) : undefined,
max_dup_msgs_per_min: set03 ? parseInt(split03[1]) : undefined,
peer_discovery: peerDiscovery ? {
enabled: peerDiscovery[0] === 'true' ? true : false,
interval: parseInt(peerDiscovery[1])
} : undefined
};
}
modifyUser = await askForInput('Modify user section? [y/N]', 'n');
if (modifyUser === 'y' || modifyUser === 'Y') {
idleTimeout = await askForInput('Idle timeout?(optional)');
set01 = await askForInput('Comma seperated max_bytes_per_msg, max_bytes_per_min and max_bad_msgs_per_min?(optional)');
if (set01) {
split01 = set01.split(',');
if (split01.length == 0 || split01.length !== 3) {
console.error('Make sure to add all three. Eg: 1,1,1');
break;
}
}
set02 = await askForInput('Comma seperated max_connections, max_in_connections_per_host and concurrent_read_reqeuests?(optional)');
if (set02) {
split02 = set02.split(',');
if (split02.length == 0 || split02.length !== 3) {
console.error('Make sure to add all three. Eg: 1,1,1');
break;
}
}
config.user = {
idle_timeout: idleTimeout ? parseInt(idleTimeout) : undefined,
max_bytes_per_msg: set01 ? parseInt(split01[0]) : undefined,
max_bytes_per_min: set01 ? parseInt(split01[1]) : undefined,
max_bad_msgs_per_min: set01 ? parseInt(split01[2]) : undefined,
max_connections: set02 ? parseInt(split02[0]) : undefined,
max_in_connections_per_host: set02 ? parseInt(split02[1]) : undefined,
concurrent_read_requests: set02 ? parseInt(split02[2]) : undefined
};
}
modifyHpfs = await askForInput('Modify hpfs section? [y/N]', 'n');
if (modifyHpfs === 'y' || modifyHpfs === 'Y') {
logLevel = await askForInput('Hpfs log level?(optional)');
config.hpfs = logLevel ? {
log_level: logLevel ? logLevel : undefined
} : undefined;
}
modifyLogs = await askForInput('Modify log section? [y/N]', 'n');
if (modifyLogs === 'y' || modifyLogs === 'Y') {
logLevel = await askForInput('HP log level?(optional)');
set01 = await askForInput('Comma seperated max_mbytes_per_file and max_file_count?(optional)');
if (set01) {
split01 = set01.split(',');
if (split01.length == 0 || split01.length !== 2) {
console.error('Make sure to add all two. Eg: 1,1');
break;
}
}
loggers = await askForInput('Comma seperated loggers?(optional)');
config.log = {
log_level: logLevel ? logLevel : undefined,
max_mbytes_per_file: set01 ? parseInt(split01[0]) : undefined,
max_file_count: set01 ? parseInt(split01[1]) : undefined,
loggers: loggers ? loggers.split(',') : undefined
};
}
sendToAgent(JSON.stringify({
type: 'initiate',
container_name: containerName,
config: config
}));
break;
case 'destroy':
containerName = await askForInput('Container Name');
sendToAgent(JSON.stringify({
type: 'destroy',
container_name: containerName
}))
break;
case 'start':
containerName = await askForInput('Container Name');
sendToAgent(JSON.stringify({
type: 'start',
container_name: containerName
}))
break;
case 'stop':
containerName = await askForInput('Container Name');
sendToAgent(JSON.stringify({
type: 'stop',
container_name: containerName
}))
break;
default:
console.error('Invalid command. Only valid [create, initiate, destroy, start and stop]');
break;
}
}
inputPump();
})
}
inputPump();
}
const sendToAgent = (msg, res = null) => {
try {
let output = execSync(`${cliPath} json -m '${msg}'`, { stdio: 'pipe' });
let message = Buffer.from(output).toString();
message = JSON.parse(message.substring(0, message.length - 2)); // Skipping the \n from the result.
console.log('Received: ', message);
res && res.status((message.content && typeof message.content == 'string' && message.content.endsWith("error")) ? 500 : 200).send(message);
}
catch (e) {
console.error(`Message sending error. ${e}`);
res && res.status(500).send(`Message sending error. ${e}`);
}
}
const checkAgentStatus = (res = null) => {
try {
let output = execSync(`${cliPath} status`, { stdio: 'pipe' });
let message = Buffer.from(output).toString();
message = message.substring(0, message.length - 1); // Skipping the \n from the result.
console.log(`Socket ${message} is online.`);
res && res.status(200).send(message);
return true;
}
catch (e) {
console.error(`Socket is offline. ${e}`);
res && res.status(500).send(`Socket is offline. ${e}`);
return false;
}
}
const webServerProtocol = (process.env.SSL === "true") ? "https" : "http";
const webServerPort = 5001;
const restApi = async () => {
// Generate tls keys if not found.
if (webServerProtocol == "https" && !fs.existsSync('./tlskey.pem')) {
console.log("TLS key files not detected. Generating..");
execSync("openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout tlskey.pem -out tlscert.pem -subj \"/C=SA/ST=SA/L=SA/O=SA/CN=SA\"");
console.log("New tls key files generated.")
}
const app = express();
app.use(express.json());
app.post("/status", (req, res) => {
checkAgentStatus(res);
});
app.post("/create", (req, res) => {
const msg = {
id,
type: 'create',
owner_pubkey: req.body.owner_pubkey,
contract_id: (req.body.contract_id === "") ? uuidv4() : req.body.contract_id,
image: req.body.image ? req.body.image : "hp.0.5-ubt.20.04"
};
sendToAgent(JSON.stringify(msg), res);
});
app.post("/initiate", (req, res) => {
const msg = {
id,
type: 'initiate',
container_name: req.body.container_name,
peers: req.body.peers ? req.body.peers : [],
unl: req.body.unl ? req.body.unl : [],
role: req.body.role ? req.body.role : 'validator',
history: req.body.history ? req.body.history : 'custom',
max_primary_shards: req.body.max_primary_shards ? req.body.max_primary_shards : 1,
max_raw_shards: req.body.max_raw_shards ? req.body.max_raw_shards : 1
};
sendToAgent(JSON.stringify(msg), res);
});
app.post("/start", (req, res) => {
const msg = {
id,
type: 'start',
container_name: req.body.container_name
};
sendToAgent(JSON.stringify(msg), res);
});
app.post("/stop", (req, res) => {
const msg = {
id,
type: 'stop',
container_name: req.body.container_name
};
sendToAgent(JSON.stringify(msg), res);
});
app.post("/destroy", (req, res) => {
const msg = {
id,
type: 'destroy',
container_name: req.body.container_name
};
sendToAgent(JSON.stringify(msg), res);
});
restServer = (webServerProtocol == "https") ?
https.createServer({
key: fs.readFileSync('./tlskey.pem'),
cert: fs.readFileSync('./tlscert.pem')
}, app) :
http.createServer(app);
restServer.listen(webServerPort, () => console.log(`Web server listening at ${webServerProtocol}://localhost:${webServerPort}`));
}
(async () => {
if (checkAgentStatus()) {
await restApi();
await interatctiveInterface();
}
})();