mirror of
https://github.com/Retropex/apolloapi-v2.git
synced 2025-06-04 08:22:33 +02:00
commit
538da8ac95
3
.gitignore
vendored
3
.gitignore
vendored
@ -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
|
||||||
|
@ -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
|
@ -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
|
||||||
|
@ -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\":
|
||||||
{
|
{
|
||||||
|
13
migrations/20240415123117_node_extra_options.js
Normal file
13
migrations/20240415123117_node_extra_options.js
Normal 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');
|
||||||
|
});
|
||||||
|
};
|
@ -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",
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
111
src/init.js
111
src/init.js
@ -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);
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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);
|
||||||
|
@ -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')
|
||||||
|
@ -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')
|
||||||
|
@ -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 }) => {
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
202
src/utils.js
202
src/utils.js
@ -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);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user