Merge pull request #6 from jstefanop/dev

Merge 2.0.5
This commit is contained in:
jstefanop 2024-05-28 13:13:52 -04:00 committed by GitHub
commit 538da8ac95
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 354 additions and 195 deletions

3
.gitignore vendored
View File

@ -60,6 +60,7 @@ typings/
# dotenv environment variables file # dotenv environment variables file
.env .env
.env.staging
.env*.local .env*.local
# next.js build output # next.js build output
@ -67,6 +68,7 @@ typings/
# sqlite # sqlite
*.sqlite *.sqlite
*.sqlite.*
#backed #backed
backend/apollo-miner/apollo-miner* backend/apollo-miner/apollo-miner*
@ -77,3 +79,4 @@ backend/node/bitcoind
backend/node/bitcoin.conf backend/node/bitcoin.conf
backend/ckpool/ckpool.conf backend/ckpool/ckpool.conf
backend/start_swap.sh backend/start_swap.sh
backend/apollo-miner/mode

View File

@ -2,7 +2,8 @@ server=1
rpcuser=futurebit rpcuser=futurebit
rpcpassword= rpcpassword=
daemon=0 daemon=0
maxconnections=32
upnp=1 upnp=1
uacomment=FutureBit-Apollo-Node uacomment=FutureBit-Apollo-Node
#SOLO_START
zmqpubhashblock=tcp://127.0.0.1:28332 zmqpubhashblock=tcp://127.0.0.1:28332
#SOLO_END

View File

@ -2,6 +2,5 @@ server=1
rpcuser=futurebit rpcuser=futurebit
rpcpassword=futurebit rpcpassword=futurebit
daemon=0 daemon=0
maxconnections=32
upnp=1 upnp=1
uacomment=FutureBit-Apollo-Node uacomment=FutureBit-Apollo-Node

View File

@ -50,12 +50,47 @@ then
MINER_FAN_SPEED=$(cat /var/local/apollo/hwmon/fan_rpm) MINER_FAN_SPEED=$(cat /var/local/apollo/hwmon/fan_rpm)
fi fi
#Network # Function to check if an interface is up or down
get_interface_status() {
local interface=$1
ip link show "$interface" | grep -q "state UP"
if [ $? -eq 0 ]; then
echo "true"
else
echo "false"
fi
}
# Retrieve IP, MAC and status of eth0 and wlan0 interfaces
ETH_IP=$(ip addr show eth0 | awk '/inet / {print $2}' | cut -d/ -f 1) ETH_IP=$(ip addr show eth0 | awk '/inet / {print $2}' | cut -d/ -f 1)
ETH_MAC=$(ip link show eth0 | awk '/ether/ {print $2}') ETH_MAC=$(ip link show eth0 | awk '/ether/ {print $2}')
ETH_STATUS=$(get_interface_status eth0)
WLAN_IP=$(ip addr show wlan0 | awk '/inet / {print $2}' | cut -d/ -f 1) WLAN_IP=$(ip addr show wlan0 | awk '/inet / {print $2}' | cut -d/ -f 1)
WLAN_MAC=$(ip link show wlan0 | awk '/ether/ {print $2}') WLAN_MAC=$(ip link show wlan0 | awk '/ether/ {print $2}')
WLAN_STATUS=$(get_interface_status wlan0)
# Variable to track the first IP of wlx interfaces
FIRST_WLX_IP=""
FIRST_WLX_MAC=""
FIRST_WLX_STATUS=""
# Retrieve IP, MAC and status of WiFi interfaces starting with wlx
for interface in $(ip -o link show | awk -F': ' '{print $2}' | grep ^wlx); do
WLX_IP=$(ip addr show "$interface" | awk '/inet / {print $2}' | cut -d/ -f 1)
WLX_MAC=$(ip link show "$interface" | awk '/ether/ {print $2}')
WLX_STATUS=$(get_interface_status "$interface")
# If WLAN_IP is empty and FIRST_WLX_IP is not yet assigned, use WLX_IP
if [ -z "$WLAN_IP" ] && [ -z "$FIRST_WLX_IP" ] && [ -n "$WLX_IP" ]; then
FIRST_WLX_IP=$WLX_IP
FIRST_WLX_MAC=$WLX_MAC
FIRST_WLX_STATUS=$WLX_STATUS
WLAN_IP=$WLX_IP
WLAN_MAC=$WLX_MAC
WLAN_STATUS=$WLX_STATUS
fi
done
# Memory # Memory
memTotal=$(egrep '^MemTotal:' /proc/meminfo | awk '{print $2}') memTotal=$(egrep '^MemTotal:' /proc/meminfo | awk '{print $2}')
@ -98,11 +133,13 @@ JSON="{
\"network\": [{ \"network\": [{
\"name\": \"eth0\", \"name\": \"eth0\",
\"address\": \"$ETH_IP\", \"address\": \"$ETH_IP\",
\"mac\": \"$ETH_MAC\" \"mac\": \"$ETH_MAC\",
\"status\": \"$ETH_STATUS\"
}, { }, {
\"name\": \"wlan0\", \"name\": \"wlan0\",
\"address\": \"$WLAN_IP\", \"address\": \"$WLAN_IP\",
\"mac\": \"$WLAN_MAC\" \"mac\": \"$WLAN_MAC\",
\"status\": \"$WLAN_STATUS\"
}], }],
\"memory\": \"memory\":
{ {

View File

@ -0,0 +1,13 @@
exports.up = function (knex, Promise) {
return knex.schema.table('settings', function (t) {
t.integer('node_max_connections').defaultTo(32);
t.boolean('node_allow_lan').defaultTo(false);
});
};
exports.down = function (knex, Promise) {
return knex.schema.table('settings', function (t) {
t.dropColumn('node_max_connections');
t.dropColumn('node_allow_lan');
});
};

View File

@ -1,6 +1,6 @@
{ {
"name": "apolloapi-v2", "name": "apolloapi-v2",
"version": "2.0.4", "version": "2.0.5",
"main": "index.js", "main": "index.js",
"repository": "https://github.com/jstefanop/apolloapi-v2.git", "repository": "https://github.com/jstefanop/apolloapi-v2.git",
"author": "FutureBit LLC", "author": "FutureBit LLC",

View File

@ -17,6 +17,7 @@ module.exports.typeDefs = `
uuid: String uuid: String
version: String version: String
date: String date: String
comport: String
statVersion: String statVersion: String
versions: MinerStatsVersion versions: MinerStatsVersion
master: MinerStatsMaster master: MinerStatsMaster
@ -62,7 +63,7 @@ module.exports.typeDefs = `
byDiff: Float byDiff: Float
byPool: Float byPool: Float
byJobs: Float byJobs: Float
solutions: Int solutions: Float
errors: Int errors: Int
errorRate: Float errorRate: Float
chipSpeed: Float chipSpeed: Float
@ -156,8 +157,8 @@ module.exports.typeDefs = `
lowCurrRst: Int lowCurrRst: Int
currents: [Int] currents: [Int]
brokenPwc: Int brokenPwc: Int
solutions: Int solutions: Float
errors: Int errors: Float
ghs: Float ghs: Float
errorRate: Float errorRate: Float
chipRestarts: Int chipRestarts: Int
@ -185,11 +186,11 @@ module.exports.typeDefs = `
type MinerStatsCkpool { type MinerStatsCkpool {
pool: MinerStatsCkpoolPool pool: MinerStatsCkpoolPool
users: MinerStatsCkpoolUsers users: [MinerStatsCkpoolUsers]
} }
type MinerStatsCkpoolPool { type MinerStatsCkpoolPool {
runtime: Int runtime: Float
lastupdate: Int lastupdate: Int
Users: Int Users: Int
Workers: Int Workers: Int

View File

@ -23,11 +23,13 @@ module.exports.typeDefs = `
leftSidebarExtended: Boolean! leftSidebarExtended: Boolean!
rightSidebarVisibility: Boolean! rightSidebarVisibility: Boolean!
temperatureUnit: TemperatureUnit! temperatureUnit: TemperatureUnit!
powerLedOff: Boolean
nodeRpcPassword: String nodeRpcPassword: String
nodeEnableTor: Boolean nodeEnableTor: Boolean
nodeUserConf: String nodeUserConf: String
nodeEnableSoloMining: Boolean nodeEnableSoloMining: Boolean
powerLedOff: Boolean nodeMaxConnections: Int
nodeAllowLan: Boolean
} }
` `

View File

@ -18,11 +18,13 @@ module.exports.typeDefs = `
leftSidebarExtended: Boolean leftSidebarExtended: Boolean
rightSidebarVisibility: Boolean rightSidebarVisibility: Boolean
temperatureUnit: TemperatureUnit temperatureUnit: TemperatureUnit
powerLedOff: Boolean
nodeRpcPassword: String nodeRpcPassword: String
nodeEnableTor: Boolean nodeEnableTor: Boolean
nodeUserConf: String nodeUserConf: String
nodeEnableSoloMining: Boolean nodeEnableSoloMining: Boolean
powerLedOff: Boolean nodeMaxConnections: Int
nodeAllowLan: Boolean
} }
type SettingsUpdateOutput { type SettingsUpdateOutput {

View File

@ -1,12 +1,8 @@
const { writeFileSync, readFileSync, existsSync } = require('fs'); const { writeFileSync, existsSync } = require('fs');
const { join } = require('path'); const { join } = require('path');
const crypto = require('crypto'); const crypto = require('crypto');
const { exec } = require('child_process');
const generator = require('generate-password');
const utils = require('./utils'); const utils = require('./utils');
const { knex } = require('./db'); const { knex } = require('./db');
const fs = require('fs').promises;
const path = require('path');
const initEnvFile = async () => { const initEnvFile = async () => {
const envPath = join(__dirname, '..', '.env'); const envPath = join(__dirname, '..', '.env');
@ -30,109 +26,38 @@ const initEnvFile = async () => {
const runMigrations = async () => { const runMigrations = async () => {
try { try {
console.log('Run migrations'); console.log('Run migrations');
const resp = await knex.migrate.latest(); await knex.migrate.latest();
await createCkpoolConfigFile(); const [settings] = await knex('settings')
await createBitcoinConfigFile(); .select([
await runGenerateBitcoinPassword(); 'node_rpc_password as nodeRpcPassword',
'node_enable_tor as nodeEnableTor',
'node_user_conf as nodeUserConf',
'node_enable_solo_mining as nodeEnableSoloMining',
'node_max_connections as nodeMaxConnections',
'node_allow_lan as nodeAllowLan',
])
.orderBy('created_at', 'desc')
.orderBy('id', 'desc')
.limit(1);
await utils.auth.manageBitcoinConf(settings);
await runGenerateBitcoinPassword(settings);
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
}; };
const runGenerateBitcoinPassword = async () => { const runGenerateBitcoinPassword = async (settings) => {
try { try {
console.log('Checking bitcoin password existence'); console.log('Checking bitcoin password existence');
const [settings] = await knex('settings')
.select(['node_rpc_password as nodeRpcPassword'])
.orderBy('created_at', 'desc')
.orderBy('id', 'desc')
.limit(1);
if (settings && settings.nodeRpcPassword) if (settings && settings.nodeRpcPassword)
return console.log('Bitcoin password found'); return console.log('Bitcoin password found');
else await utils.auth.changeNodeRpcPassword(); else await utils.auth.changeNodeRpcPassword(settings);
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
}; };
const createCkpoolConfigFile = async () => {
const configFilePath = path.resolve(
__dirname,
'../backend/ckpool/ckpool.conf'
);
const configContent = `{
"btcd": [
{
"url": "127.0.0.1:8332",
"auth": "futurebit",
"pass": "",
"notify": true
}
],
"logdir": "/opt/apolloapi/backend/ckpool/logs"
}`;
try {
// Check if the file exists
await fs.access(configFilePath);
console.log('File ckpool.conf already exists.');
} catch (error) {
try {
// Create the file
await fs.writeFile(configFilePath, configContent, 'utf-8');
console.log('File ckpool.conf created.');
} catch (error) {
console.error(
`Error during the creation of the file ckpool.conf: ${error.message}`
);
}
} finally {
const [settings] = await knex('settings')
.select(['node_rpc_password as nodeRpcPassword'])
.orderBy('created_at', 'desc')
.orderBy('id', 'desc')
.limit(1);
if (settings && settings.nodeRpcPassword) {
exec(
`sudo sed -i 's#"pass":.*#"pass": "${settings.nodeRpcPassword}",#g' ${configFilePath}`
);
}
}
};
const createBitcoinConfigFile = async () => {
const configFilePath = path.resolve(
__dirname,
'../backend/node/bitcoin.conf'
);
const configContent = `server=1
rpcuser=futurebit
rpcpassword=
daemon=0
maxconnections=32
upnp=1
uacomment=FutureBit-Apollo-Node`;
try {
// Check if the file exists
await fs.access(configFilePath);
console.log('File bitcoin.conf already exists.');
} catch (error) {
try {
// Create the file
await fs.writeFile(configFilePath, configContent, 'utf-8');
console.log('File bitcoin.conf created.');
await utils.auth.changeNodeRpcPassword();
} catch (error) {
console.error(
`Error during the creation of the file bitcoin.conf: ${error.message}`
);
}
}
};
initEnvFile(); initEnvFile();
runMigrations().then(startServer); runMigrations().then(startServer);

View File

@ -11,7 +11,6 @@ module.exports = ({ define }) => {
}) })
utils.auth.changeSystemPassword(password) utils.auth.changeSystemPassword(password)
await utils.auth.changeNodeRpcPassword()
} catch (err) { } catch (err) {
console.log('ERROR', err); console.log('ERROR', err);
} }

View File

@ -1,15 +1,30 @@
const { join } = require('path');
const { exec } = require('child_process');
const fs = require('fs').promises; const fs = require('fs').promises;
const path = require('path');
const _ = require('lodash');
module.exports = ({ define }) => { module.exports = ({ define }) => {
define( define(
'updateProgress', 'updateProgress',
async (payload, { knex, errors, utils }) => { async (payload, { knex, errors, utils }) => {
try { try {
const data = await fs.readFile(`/tmp/update_progress`); // 1. Check if the file exists
const filePath = '/tmp/update_progress';
let fileExists = true;
try {
await fs.access(filePath, fs.constants.F_OK);
} catch (error) {
if (error.code === 'ENOENT') {
// File doesn't exist
console.log('update_progress file not found. Returning default progress.');
fileExists = false;
} else {
throw error;
}
}
if (!fileExists) {
return { value: 0 };
}
const data = await fs.readFile(filePath);
const progress = parseInt(data.toString()); const progress = parseInt(data.toString());
return { value: progress }; return { value: progress };
} catch (error) { } catch (error) {

View File

@ -1,10 +1,7 @@
const { join } = require('path');
const { exec } = require('child_process');
const fs = require('fs').promises; const fs = require('fs').promises;
const path = require('path'); const path = require('path');
const _ = require('lodash'); const _ = require('lodash');
const moment = require('moment'); const moment = require('moment');
const { existsSync } = require('fs');
const c = require('config'); const c = require('config');
module.exports = ({ define }) => { module.exports = ({ define }) => {
@ -69,40 +66,48 @@ const getCkpoolStats = async (errors, settings, pools) => {
try { try {
if (settings?.nodeEnableSoloMining) { if (settings?.nodeEnableSoloMining) {
const poolUsername = pools[0] && pools[0].username;
const ckpoolPoolStatsFile = path.resolve( const ckpoolPoolStatsFile = path.resolve(
__dirname, __dirname,
'../../../../backend/ckpool/logs/pool/pool.status' '../../../../backend/ckpool/logs/pool/pool.status'
); );
const ckpoolUsersStatsFile = path.resolve( const ckpoolUsersStatsDir = path.resolve(
__dirname, __dirname,
`../../../../backend/ckpool/logs/users/${poolUsername}` '../../../../backend/ckpool/logs/users/'
); );
if ( try {
existsSync(ckpoolPoolStatsFile) && // Check if the directory exists
existsSync(ckpoolUsersStatsFile) await fs.stat(ckpoolUsersStatsDir);
) { } catch (err) {
await Promise.all([ if (err.code === 'ENOENT') {
(async () => { // Directory does not exist
let ckpoolPoolData = await parseFileToJsonArray( resolve(ckpoolData); // Resolve with null data
ckpoolPoolStatsFile return;
}
throw err; // Re-throw other errors
}
const filenames = await fs.readdir(ckpoolUsersStatsDir);
const usersDataPromises = filenames.map(async (filename) => {
const ckpoolUsersStatsFile = path.resolve(
ckpoolUsersStatsDir,
filename
); );
let ckpoolUsersData = await fs.readFile( const ckpoolUsersData = await fs.readFile(
ckpoolUsersStatsFile, ckpoolUsersStatsFile,
'utf8' 'utf8'
); );
return JSON.parse(ckpoolUsersData);
});
ckpoolUsersData = JSON.parse(ckpoolUsersData); const usersData = await Promise.all(usersDataPromises);
ckpoolData = { ckpoolData = {
pool: ckpoolPoolData, pool: await parseFileToJsonArray(ckpoolPoolStatsFile),
users: ckpoolUsersData, users: usersData,
}; };
})(),
]);
}
} }
resolve(ckpoolData); resolve(ckpoolData);

View File

@ -28,11 +28,13 @@ module.exports = ({ define }) => {
'left_sidebar_extended as leftSidebarExtended', 'left_sidebar_extended as leftSidebarExtended',
'right_sidebar_visibility as rightSidebarVisibility', 'right_sidebar_visibility as rightSidebarVisibility',
'temperature_unit as temperatureUnit', 'temperature_unit as temperatureUnit',
'power_led_off as powerLedOff',
'node_rpc_password as nodeRpcPassword', 'node_rpc_password as nodeRpcPassword',
'node_enable_tor as nodeEnableTor', 'node_enable_tor as nodeEnableTor',
'node_user_conf as nodeUserConf', 'node_user_conf as nodeUserConf',
'node_enable_solo_mining as nodeEnableSoloMining', 'node_enable_solo_mining as nodeEnableSoloMining',
'power_led_off as powerLedOff', 'node_max_connections as nodeMaxConnections',
'node_allow_lan as nodeAllowLan',
) )
readQ.orderBy('created_at', 'desc') readQ.orderBy('created_at', 'desc')

View File

@ -13,11 +13,13 @@ module.exports = ({ define }) => {
'left_sidebar_extended as leftSidebarExtended', 'left_sidebar_extended as leftSidebarExtended',
'right_sidebar_visibility as rightSidebarVisibility', 'right_sidebar_visibility as rightSidebarVisibility',
'temperature_unit as temperatureUnit', 'temperature_unit as temperatureUnit',
'power_led_off as powerLedOff',
'node_rpc_password as nodeRpcPassword', 'node_rpc_password as nodeRpcPassword',
'node_enable_tor as nodeEnableTor', 'node_enable_tor as nodeEnableTor',
'node_user_conf as nodeUserConf', 'node_user_conf as nodeUserConf',
'node_enable_solo_mining as nodeEnableSoloMining', 'node_enable_solo_mining as nodeEnableSoloMining',
'power_led_off as powerLedOff', 'node_max_connections as nodeMaxConnections',
'node_allow_lan as nodeAllowLan',
]) ])
.orderBy('created_at', 'desc') .orderBy('created_at', 'desc')
.orderBy('id', 'desc') .orderBy('id', 'desc')

View File

@ -12,11 +12,13 @@ const updateFields = {
leftSidebarExtended: 'left_sidebar_extended', leftSidebarExtended: 'left_sidebar_extended',
rightSidebarVisibility: 'right_sidebar_visibility', rightSidebarVisibility: 'right_sidebar_visibility',
temperatureUnit: 'temperature_unit', temperatureUnit: 'temperature_unit',
powerLedOff: 'power_led_off',
nodeRpcPassword: 'node_rpc_password', nodeRpcPassword: 'node_rpc_password',
nodeEnableTor: 'node_enable_tor', nodeEnableTor: 'node_enable_tor',
nodeUserConf: 'node_user_conf', nodeUserConf: 'node_user_conf',
nodeEnableSoloMining: 'node_enable_solo_mining', nodeEnableSoloMining: 'node_enable_solo_mining',
powerLedOff: 'power_led_off' nodeMaxConnections: 'node_max_connections',
nodeAllowLan: 'node_allow_lan'
} }
module.exports = ({ define }) => { module.exports = ({ define }) => {

View File

@ -11,7 +11,10 @@ module.exports = ({ define }) => {
if ( if (
oldSettings.nodeEnableTor !== newSettings.nodeEnableTor || oldSettings.nodeEnableTor !== newSettings.nodeEnableTor ||
oldSettings.nodeUserConf !== newSettings.nodeUserConf || oldSettings.nodeUserConf !== newSettings.nodeUserConf ||
oldSettings.nodeEnableSoloMining !== newSettings.nodeEnableSoloMining oldSettings.nodeEnableSoloMining !== newSettings.nodeEnableSoloMining ||
oldSettings.nodeRpcPassword !== newSettings.nodeRpcPassword ||
oldSettings.nodeAllowLan !== newSettings.nodeAllowLan ||
oldSettings.nodeMaxConnections !== newSettings.nodeMaxConnections
) )
await utils.auth.manageBitcoinConf(newSettings); await utils.auth.manageBitcoinConf(newSettings);

View File

@ -20,7 +20,7 @@ const store = loadStore({
return !['index.js', 'store.js'].includes(relativePath) && relativePath.match(/\.js$/) return !['index.js', 'store.js'].includes(relativePath) && relativePath.match(/\.js$/)
} }
}, },
logger: logger, logger: process.env.NODE_ENV !== 'development' && logger,
methodContext: { methodContext: {
knex, knex,
utils utils

View File

@ -6,6 +6,22 @@ const config = require('config');
const { knex } = require('./db'); const { knex } = require('./db');
const fsPromises = require('fs').promises; const fsPromises = require('fs').promises;
const path = require('path'); 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 = { module.exports.auth = {
hashPassword(password) { hashPassword(password) {
@ -23,7 +39,7 @@ module.exports.auth = {
exec(`echo 'futurebit:${password}' | sudo chpasswd`); exec(`echo 'futurebit:${password}' | sudo chpasswd`);
}, },
async changeNodeRpcPassword() { async changeNodeRpcPassword(settings) {
try { try {
console.log('Generating and saving bitcoin password'); console.log('Generating and saving bitcoin password');
@ -36,28 +52,20 @@ module.exports.auth = {
node_rpc_password: password, node_rpc_password: password,
}); });
const configFilePath = path.resolve( await fsPromises.access(configBitcoinFilePath);
__dirname, await fsPromises.access(configCkpoolFilePath);
'../backend/node/bitcoin.conf'
);
const configCkpoolFilePath = path.resolve( exec(
__dirname, `sudo sed -i 's/rpcpassword.*/rpcpassword=${password}/g' ${configBitcoinFilePath}`
'../backend/ckpool/ckpool.conf'
); );
exec( exec(
`sudo sed -i s/rpcpassword.*/rpcpassword=${password}/g ${configFilePath}` `sudo sed -i 's#"pass":.*#"pass": "${password}",#g' ${configCkpoolFilePath}`
); );
exec(
`sudo sed -i 's#"pass": ""#"pass": "${password}"#g' ${configCkpoolFilePath}`
);
console.log(password, configFilePath);
exec('sudo systemctl restart node'); exec('sudo systemctl restart node');
exec('sudo systemctl restart ckpool');
if (settings?.nodeEnableSoloMining) exec('sudo systemctl restart ckpool');
} catch (err) { } catch (err) {
console.log('ERR changeNodeRpcPassword', err); console.log('ERR changeNodeRpcPassword', err);
} }
@ -73,13 +81,83 @@ module.exports.auth = {
}; };
}, },
async manageBitcoinConf(settings) { networkAddressWithCIDR(ipAddress, netmask) {
const defaultConf = `server=1\nrpcuser=futurebit\nrpcpassword=${settings.nodeRpcPassword}\ndaemon=0\nmaxconnections=32\nupnp=1\nuacomment=FutureBit-Apollo-Node`; // Converti l'indirizzo IP e la netmask in forma binaria
let conf = defaultConf; 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('');
if (settings.nodeEnableSoloMining) { // Applica l'operazione bitwise AND
conf += `\n#SOLO_START\nzmqpubhashblock=tcp://127.0.0.1:28332\n#SOLO_END`; 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 = { const ckpoolConf = {
btcd: [ btcd: [
{ {
@ -95,12 +173,29 @@ module.exports.auth = {
}; };
await fsPromises.writeFile( await fsPromises.writeFile(
'/opt/apolloapi/backend/ckpool/ckpool.conf', configCkpoolFilePath,
JSON.stringify(ckpoolConf, null, 2) 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( exec(
'sudo cp /opt/app/backend/systemd/ckpool.service /etc/systemd/system/ckpool.service' `sudo cp ${configCkpoolServiceFilePath} /etc/systemd/system/ckpool.service`
); );
exec('sudo systemctl daemon-reload'); exec('sudo systemctl daemon-reload');
exec('sudo systemctl enable ckpool'); exec('sudo systemctl enable ckpool');
@ -119,13 +214,66 @@ module.exports.auth = {
exec('sudo systemctl disable tor'); exec('sudo systemctl disable tor');
} }
if (settings.nodeUserConf) if (settings.nodeMaxConnections)
conf += `\n#USER_INPUT_START\n${settings.nodeUserConf}\n#USER_INPUT_END`; 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); console.log('Writing Bitcoin conf file', conf);
exec(`echo "${conf}" | sudo tee /opt/apolloapi/backend/node/bitcoin.conf`); await fsPromises.writeFile(configBitcoinFilePath, conf);
exec('sudo systemctl restart node'); exec('sleep 3 && sudo systemctl restart node');
} catch (err) {
console.log('ERR manageBitcoinConf', err);
}
}, },
}; };