apolloapi-v2/src/utils.js
2024-05-28 07:38:50 +02:00

280 lines
8.3 KiB
JavaScript

const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const { exec } = require('child_process');
const generator = require('generate-password');
const config = require('config');
const { knex } = require('./db');
const fsPromises = require('fs').promises;
const path = require('path');
const os = require('os');
const configBitcoinFilePath = path.resolve(
__dirname,
'../backend/node/bitcoin.conf'
);
const configCkpoolFilePath = path.resolve(
__dirname,
'../backend/ckpool/ckpool.conf'
);
const configCkpoolServiceFilePath = path.resolve(
__dirname,
'../backend/systemd/ckpool.service'
);
module.exports.auth = {
hashPassword(password) {
return bcrypt.hash(password, 12);
},
comparePassword(password, hash) {
if (!password || !hash) {
return false;
}
return bcrypt.compare(password, hash);
},
changeSystemPassword(password) {
exec(`echo 'futurebit:${password}' | sudo chpasswd`);
},
async changeNodeRpcPassword(settings) {
try {
console.log('Generating and saving bitcoin password');
const password = generator.generate({
length: 12,
numbers: true,
});
await knex('settings').update({
node_rpc_password: password,
});
await fsPromises.access(configBitcoinFilePath);
await fsPromises.access(configCkpoolFilePath);
exec(
`sudo sed -i 's/rpcpassword.*/rpcpassword=${password}/g' ${configBitcoinFilePath}`
);
exec(
`sudo sed -i 's#"pass":.*#"pass": "${password}",#g' ${configCkpoolFilePath}`
);
exec('sudo systemctl restart node');
if (settings?.nodeEnableSoloMining) exec('sudo systemctl restart ckpool');
} catch (err) {
console.log('ERR changeNodeRpcPassword', err);
}
},
generateAccessToken() {
const accessToken = jwt.sign({}, config.get('server.secret'), {
subject: 'apollouser',
audience: 'auth',
});
return {
accessToken,
};
},
networkAddressWithCIDR(ipAddress, netmask) {
// Converti l'indirizzo IP e la netmask in forma binaria
const ipBinary = ipAddress
.split('.')
.map((part) => parseInt(part, 10).toString(2).padStart(8, '0'))
.join('');
const netmaskBinary = netmask
.split('.')
.map((part) => parseInt(part, 10).toString(2).padStart(8, '0'))
.join('');
// Applica l'operazione bitwise AND
const networkBinary = ipBinary
.split('')
.map((bit, index) => bit & netmaskBinary[index])
.join('');
// Converti il risultato in forma di stringa
const networkAddress = networkBinary
.match(/.{1,8}/g)
.map((byte) => parseInt(byte, 2))
.join('.');
// Calcola il numero di bit della netmask
const cidrPrefix = netmask
.split('.')
.reduce(
(acc, byte) =>
acc + (parseInt(byte, 10).toString(2).match(/1/g) || '').length,
0
);
return `${networkAddress}/${cidrPrefix}`;
},
getSystemNetwork() {
// Get network interface information
const interfaces = os.networkInterfaces();
let address = null;
let netmask = null;
let network = null;
// Check if wlan0 has an associated IP address
if (
interfaces['wlan0'] &&
interfaces['wlan0'].some((info) => info.family === 'IPv4')
) {
// If wlan0 has an associated IP address, use wlan0
address = interfaces['wlan0'].find(
(info) => info.family === 'IPv4'
).address;
netmask = interfaces['wlan0'].find(
(info) => info.family === 'IPv4'
).netmask;
} else if (
interfaces['eth0'] &&
interfaces['eth0'].some((info) => info.family === 'IPv4')
) {
// If wlan0 doesn't have an associated IP address but eth0 does, use eth0
address = interfaces['eth0'].find(
(info) => info.family === 'IPv4'
).address;
netmask = interfaces['eth0'].find(
(info) => info.family === 'IPv4'
).netmask;
} else {
console.log('No IP address associated with wlan0 or eth0');
}
if (address && netmask)
network = this.networkAddressWithCIDR(address, netmask);
return network;
},
async manageCkpoolConf(settings) {
try {
const ckpoolConf = {
btcd: [
{
url: '127.0.0.1:8332',
auth: 'futurebit',
pass: settings.nodeRpcPassword,
notify: true,
},
],
logdir: '/opt/apolloapi/backend/ckpool/logs',
btcsig: '/mined by Solo FutureBit Apollo/',
zmqblock: 'tcp://127.0.0.1:28332',
};
await fsPromises.writeFile(
configCkpoolFilePath,
JSON.stringify(ckpoolConf, null, 2)
);
} catch (err) {
console.log('ERR manageCkpoolConf', err);
}
},
async manageBitcoinConf(settings) {
try {
// CHecking current conf file
const currentConf = await fsPromises.readFile(configBitcoinFilePath, 'utf8');
const currentConfBase64 = Buffer.from(currentConf).toString('base64');
const defaultConf = `server=1\nrpcuser=futurebit\nrpcpassword=${settings.nodeRpcPassword}\ndaemon=0\nupnp=1\nuacomment=FutureBit-Apollo-Node`;
let conf = defaultConf;
conf += `\n#SOLO_START\nzmqpubhashblock=tcp://127.0.0.1:28332\n#SOLO_END`;
this.manageCkpoolConf(settings);
if (settings.nodeEnableSoloMining) {
exec(
`sudo cp ${configCkpoolServiceFilePath} /etc/systemd/system/ckpool.service`
);
exec('sudo systemctl daemon-reload');
exec('sudo systemctl enable ckpool');
exec('sudo systemctl restart ckpool');
} else {
exec('sudo systemctl stop ckpool');
exec('sudo systemctl disable ckpool');
}
if (settings.nodeEnableTor) {
conf += `\n#TOR_START\nproxy=127.0.0.1:9050\nlisten=1\nbind=127.0.0.1\nonlynet=onion\ndnsseed=0\ndns=0\n#TOR_END`;
exec('sudo systemctl enable tor');
exec('sudo systemctl restart tor');
} else {
exec('sudo systemctl stop tor');
exec('sudo systemctl disable tor');
}
if (settings.nodeMaxConnections)
conf += `\nmaxconnections=${settings.nodeMaxConnections}`;
else conf += '\nmaxconnections=32';
if (settings.nodeAllowLan) {
const lanNetwork = this.getSystemNetwork();
conf += `\nrpcbind=0.0.0.0\nrpcallowip=0.0.0.0/0`;
}
if (settings.nodeUserConf) {
// Extract variable names from settings.nodeUserConf using regex
const userConfVariables = settings.nodeUserConf.match(/^[^=\r\n]+/gm);
// Extract variable names from defaultConf using regex
const defaultConfVariables = defaultConf.match(/^[^=\r\n]+/gm);
// Remove variables from settings.nodeUserConf that are also present in defaultConf
const filteredUserConfVariables = userConfVariables.filter(
(variable) => !defaultConfVariables.includes(variable)
);
if (filteredUserConfVariables.length) {
// Initialize an empty array to store formatted user configurations
const formattedUserConf = [];
// Iterate through filtered variables and format them
filteredUserConfVariables.forEach((variable) => {
// If the variable has a value, format it as "variable=value"
// Otherwise, format it as "variable"
const formattedVariable = settings.nodeUserConf.includes(
`${variable}=`
)
? `${variable}=${
settings.nodeUserConf.match(new RegExp(`${variable}=(.*)`))[1]
}`
: variable;
// Push the formatted variable to the array
formattedUserConf.push(formattedVariable);
});
// Join the formatted variables into a single string with newlines
const filteredUserConf = formattedUserConf.join('\n');
// Append the filtered user configuration to the overall configuration
conf += `\n#USER_INPUT_START\n${filteredUserConf}\n#USER_INPUT_END`;
}
}
const confBase64 = Buffer.from(conf).toString('base64');
if (currentConfBase64 === confBase64) return console.log('No changes to bitcoin.conf file');
console.log('Writing Bitcoin conf file', conf);
await fsPromises.writeFile(configBitcoinFilePath, conf);
exec('sleep 3 && sudo systemctl restart node');
} catch (err) {
console.log('ERR manageBitcoinConf', err);
}
},
};