Compare commits
41 Commits
abdb725964
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca2815ab8f | ||
|
|
556697725a | ||
|
|
b448f04aec | ||
|
|
fae8417b2f | ||
|
|
e06cc4762d | ||
|
|
6deba2bad2 | ||
|
|
e0d1a4a2fe | ||
| 1078faa766 | |||
|
|
75580c0547 | ||
|
|
880f1ccb01 | ||
|
|
76c4d002a0 | ||
|
|
2260deee01 | ||
|
|
8a0baa5bc9 | ||
|
|
ec091c0017 | ||
|
|
a5996ccfc0 | ||
|
|
3b248e36ec | ||
|
|
f5d58d45da | ||
|
|
62c0504028 | ||
|
|
5dd27f0bed | ||
|
|
e0bae665ed | ||
|
|
598c802b28 | ||
|
|
ace762fc76 | ||
|
|
75156d059e | ||
|
|
abb833840a | ||
|
|
11c8a149bb | ||
|
|
9c111eda1a | ||
|
|
31de3a040d | ||
|
|
318ee7bf91 | ||
|
|
5428ac6144 | ||
|
|
e27dd9d9cb | ||
|
|
c0927601b9 | ||
|
|
ef45cf6539 | ||
|
|
23bea5f74e | ||
|
|
fc743cbb46 | ||
|
|
e522326576 | ||
|
|
2e22fa66a6 | ||
|
|
6b37a48061 | ||
|
|
5d6d86fa47 | ||
|
|
c35d3f3fa7 | ||
|
|
c38bca4144 | ||
|
|
e6332dffc9 |
@@ -17,4 +17,7 @@ SERVER_HOSTNAME=""
|
||||
SERVER_PORT=3000
|
||||
|
||||
# Configuration of the local OP25 application
|
||||
OP25_BIN_PATH=""
|
||||
#OP25_BIN_PATH=""
|
||||
|
||||
# Logfile location config
|
||||
#LOG_LOCATION=""
|
||||
@@ -17,7 +17,7 @@ var { attachRadioSessionToRequest } = require('./controllers/radioController');
|
||||
const log = new DebugBuilder("client", "app");
|
||||
|
||||
var app = express();
|
||||
var port = process.env.HTTP_PORT || '3010';
|
||||
var port = process.env.CLIENT_PORT || '3010';
|
||||
|
||||
// view engine setup
|
||||
app.set('views', path.join(__dirname, 'views'));
|
||||
|
||||
@@ -8,20 +8,21 @@ async def load_opus():
|
||||
# Check the system type and load the correct library
|
||||
# Linux ARM AARCH64 running 32bit OS
|
||||
processor = platform.machine()
|
||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
print("Processor: ", processor)
|
||||
if os.name == 'nt':
|
||||
if processor == "AMD64":
|
||||
if processor == "AMD64":
|
||||
opus.load_opus(os.path.join(script_dir, './opus/libopus_amd64.dll'))
|
||||
print(f"Loaded OPUS library for AMD64")
|
||||
opus.load_opus('./opus/libopus_amd64.dll')
|
||||
return "AMD64"
|
||||
else:
|
||||
if processor == "aarch64":
|
||||
if processor == "aarch64":
|
||||
opus.load_opus(os.path.join(script_dir, './opus/libopus_aarcch64.so'))
|
||||
print(f"Loaded OPUS library for aarch64")
|
||||
opus.load_opus('./opus/libopus_aarcch64.so')
|
||||
return "aarch64"
|
||||
elif processor == "armv7l":
|
||||
elif processor == "armv7l":
|
||||
opus.load_opus(os.path.join(script_dir, './opus/libopus_armv7l.so'))
|
||||
print(f"Loaded OPUS library for armv7l")
|
||||
opus.load_opus('./opus/libopus_armv7l.so')
|
||||
return "armv7l"
|
||||
|
||||
|
||||
|
||||
@@ -30,13 +30,15 @@ function addToastToStorage(time, message) {
|
||||
var toasts = [{ 'time': time, 'message': message }]
|
||||
var storedToasts = getStoredToasts();
|
||||
console.log("Adding new notification to storage: ", toasts);
|
||||
toasts = toasts.concat(storedToasts);
|
||||
console.log("Combined new and stored notifications: ", toasts);
|
||||
toasts = toasts.filter((value, index, self) =>
|
||||
index === self.findIndex((t) => (
|
||||
t.time === value.time && t.message === value.message
|
||||
))
|
||||
)
|
||||
if (storedToasts) {
|
||||
toasts = toasts.concat(storedToasts);
|
||||
console.log("Combined new and stored notifications: ", toasts);
|
||||
toasts = toasts.filter((value, index, self) =>
|
||||
index === self.findIndex((t) => (
|
||||
t.time === value.time && t.message === value.message
|
||||
))
|
||||
)
|
||||
}
|
||||
console.log("Deduped stored notifications: ", toasts);
|
||||
localStorage.setItem("toasts", JSON.stringify(toasts));
|
||||
navbarUpdateNotificationBellCount(toasts);
|
||||
@@ -251,8 +253,8 @@ function joinServer() {
|
||||
const responseObject = JSON.parse(Http.responseText)
|
||||
console.log(Http.status);
|
||||
console.log(responseObject);
|
||||
createToast(`${responseObject.name} will join shortly`, { showNow: true });
|
||||
$("#joinModal").modal('toggle');
|
||||
createToast(`${responseObject.name} will join shortly`);
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
177
Client/setup.sh
177
Client/setup.sh
@@ -5,21 +5,81 @@ if [ "$EUID" -ne 0 ]
|
||||
then echo "Please run as root"
|
||||
exit
|
||||
fi
|
||||
|
||||
# Prompt the user for reboot confirmation
|
||||
read -p "This script will install all required components for the DRB client. Are you okay with rebooting afterward? If not, you will have to reboot later before running the applications to finish the installation. (Reboot?: y/n): " confirm
|
||||
|
||||
# Convert user input to lowercase for case-insensitive comparison
|
||||
confirm="${confirm,,}"
|
||||
|
||||
if [[ "$confirm" != "y" && "$confirm" != "yes" ]]; then
|
||||
echo "Script will not reboot."
|
||||
should_reboot=false
|
||||
else
|
||||
echo "Script will reboot after completion."
|
||||
should_reboot=true
|
||||
fi
|
||||
|
||||
echo "----- Starting Radio Node Client Install Script -----"
|
||||
|
||||
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||
|
||||
ls -ld $SCRIPT_DIR | awk '{print $3}' >> ./config/installerName
|
||||
# Copy the example env file
|
||||
cp .env.example .env
|
||||
|
||||
# Setup user for service
|
||||
useradd -M RadioNode
|
||||
usermod -s -L RadioNode
|
||||
# Copy the radio config example file
|
||||
cp config/radioPresets.json.EXAMPLE config/radioPresets.json
|
||||
|
||||
# Change the ownership of the directory to the service user
|
||||
chown RadioNode -R $SCRIPT_DIR
|
||||
echo "----- Collecting Setup Information -----"
|
||||
|
||||
# Check for updates
|
||||
apt-get update
|
||||
# Ask the user for input and store in variables
|
||||
echo " \\Client Config"
|
||||
read -p " Enter Node Name: " nodeName
|
||||
read -p " Enter Node IP: " nodeIP
|
||||
read -p " Enter Node Port: " nodePort
|
||||
read -p " Enter Node Location: " nodeLocation
|
||||
read -p " Enter Audio Device ID: " audioDeviceID
|
||||
echo " \\Server Config"
|
||||
read -p " Enter Server IP (leave blank if using hostname): " serverIP
|
||||
if [ -z "$serverIP" ]; then
|
||||
read -p " Enter Server Hostname: " serverHostname
|
||||
fi
|
||||
read -p " Enter Server Port: " serverPort
|
||||
|
||||
# Update the values in the env file using sed
|
||||
sed -i "s/^AUDIO_DEVICE_ID=\".*\"$/AUDIO_DEVICE_ID=\"$audioDeviceID\"/" .env
|
||||
sed -i "s/^CLIENT_NAME=\".*\"$/CLIENT_NAME=\"$nodeName\"/" .env
|
||||
sed -i "s/^CLIENT_IP=\".*\"$/CLIENT_IP=\"$nodeIP\"/" .env
|
||||
sed -i "s/^CLIENT_PORT=.*$/CLIENT_PORT=$nodePort/" .env
|
||||
sed -i "s/^CLIENT_LOCATION=\".*\"$/CLIENT_LOCATION=\"$nodeLocation\"/" .env
|
||||
if [ -z "$serverIP" ]; then
|
||||
sed -i "s/^SERVER_HOSTNAME=\".*\"$/SERVER_HOSTNAME=\"$serverHostname\"/" .env
|
||||
else
|
||||
sed -i "s/^SERVER_IP=\".*\"$/SERVER_IP=\"$serverIP\"/" .env
|
||||
fi
|
||||
sed -i "s/^SERVER_PORT=\".*\"$/SERVER_PORT=\"$serverPort\"/" .env
|
||||
echo "----- Config file has been updated -----"
|
||||
|
||||
# Display the updated values
|
||||
echo "----- Start of Config File -----"
|
||||
cat .env
|
||||
echo "----- End of Config File -----"
|
||||
|
||||
echo "----- Getting Dependencies -----"
|
||||
|
||||
# Install Node Repo
|
||||
# Get the CPU architecture
|
||||
cpu_arch=$(uname -m)
|
||||
|
||||
# Print the CPU architecture for verification
|
||||
echo "Detected CPU Architecture: $cpu_arch"
|
||||
|
||||
# Check if the architecture is ARMv6
|
||||
if [[ "$cpu_arch" == "armv6"* ]]; then
|
||||
echo "----- CPU Architecture is ARMv6 or compatible. -----"
|
||||
echo "----- CPU Architectre is not compatible with dependencies of this project, please use a newer CPU architecture -----"
|
||||
exit
|
||||
|
||||
curl -fsSL https://deb.nodesource.com/setup_current.x | sudo -E bash -
|
||||
|
||||
# Update the system
|
||||
@@ -27,16 +87,41 @@ apt-get update
|
||||
apt-get upgrade -y
|
||||
|
||||
# Install the necessary packages
|
||||
apt-get install -y nodejs portaudio19-dev libportaudio2 libpulse-dev pulseaudio apulse python3 python3-pip
|
||||
|
||||
# Ensure pulse audio is running
|
||||
pulseaudio
|
||||
apt-get install -y nodejs portaudio19-dev libportaudio2 libpulse-dev pulseaudio apulse python3 python3-pip git
|
||||
|
||||
# Install the node packages from the project
|
||||
npm i
|
||||
|
||||
# Install the python packages needed for the bot
|
||||
pip install -r
|
||||
pip install -r ./pdab/requirements.txt
|
||||
|
||||
echo "----- Setting up Pulse Audio -----"
|
||||
|
||||
# Ensure pulse audio is running as system so the service can see the audio device
|
||||
systemctl --global disable pulseaudio.service pulseaudio.socket
|
||||
|
||||
# Update the PulseAudio config to disable autospawning
|
||||
sed -i 's/autospawn = .*$/autospawn = no/' /etc/pulse/client.conf
|
||||
|
||||
# Add the system PulseAudio service
|
||||
echo "[Unit]
|
||||
Description=PulseAudio system server
|
||||
|
||||
[Service]
|
||||
Type=notify
|
||||
ExecStart=pulseaudio --daemonize=no --system --realtime --log-target=journal
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target" >> /etc/systemd/system/PulseAudio.service
|
||||
|
||||
# Add the root user to the pulse-access group
|
||||
usermod -aG pulse-access root
|
||||
usermod -aG pulse-access pi
|
||||
|
||||
# Enable the PulseAudio service
|
||||
systemctl enable PulseAudio.service
|
||||
|
||||
echo "----- Setting up Radio Node Service -----"
|
||||
|
||||
# Setup bot service
|
||||
echo "[Unit]
|
||||
@@ -44,32 +129,80 @@ Description=Radio Node Service
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
WorkingDirectory="$SCRIPT_DIR"
|
||||
WorkingDirectory=$SCRIPT_DIR/
|
||||
ExecStart=/usr/bin/node .
|
||||
Restart=always
|
||||
RestartDelay=10
|
||||
User=RadioNode
|
||||
Environment=DEBUG='client:*'
|
||||
Environment=\"DEBUG='client:*'\"
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target" >> /etc/systemd/system/RadioNode.service
|
||||
|
||||
# Enable the Radio Node service
|
||||
systemctl enable RadioNode.service
|
||||
|
||||
echo "----- Setting up Radio Node Update Service -----"
|
||||
|
||||
# Setup bot update service
|
||||
echo "[Unit]
|
||||
Description=Radio Node Updater Service
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
WorkingDirectory="$SCRIPT_DIR"
|
||||
WorkingDirectory=$SCRIPT_DIR/
|
||||
ExecStart=/usr/bin/bash update.sh
|
||||
Restart=on-failure
|
||||
User=RadioNode
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target" >> /etc/systemd/system/RadioNodeUpdater.service
|
||||
|
||||
# Enable the service
|
||||
systemctl enable RadioNode.service
|
||||
# Install OP25
|
||||
echo "----- Installing OP25 from Source -----"
|
||||
# Clone the OP25 Git
|
||||
cd /opt/
|
||||
git clone https://github.com/boatbod/op25.git
|
||||
cd op25
|
||||
|
||||
# Start the service
|
||||
systemctl start RadioNode.service
|
||||
# Run the OP25 install script
|
||||
bash ./install.sh
|
||||
|
||||
# Create the config file for the client or user to update later
|
||||
cp /opt/op25/op25/gr-op25_repeater/apps/p25_rtl_example.json /opt/op25/op25/gr-op25_repeater/apps/radioNodeOP25Config.json
|
||||
|
||||
# Create the OP25 service
|
||||
echo "[Unit]
|
||||
Description=OP25 Service
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
WorkingDirectory=/opt/op25/op25/gr-op25_repeater/apps
|
||||
ExecStart=./multi_rx.py -c radioNodeOP25Config.json
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target" >> /etc/systemd/system/OP25.service
|
||||
|
||||
echo "----- OP25 Setup Complete -----"
|
||||
|
||||
# Enable the OP25 service, don't start it though as the user needs to config
|
||||
systemctl enable OP25.service
|
||||
echo "----- OP25 Enabled; Please ensure to update the configuration and start the service -----"
|
||||
|
||||
# Move back to the directory that the user started in (might not be needed?)
|
||||
cd $SCRIPT_DIR
|
||||
|
||||
echo "----- Setup Complete! -----"
|
||||
|
||||
# Reboot if the user confirmed earlier
|
||||
if [ "$should_reboot" = true ]; then
|
||||
echo "To configure the app, please go to http://$nodeIP:$nodePort"
|
||||
echo "Thank you for joining the network!"
|
||||
# Prompt user to press any key before rebooting
|
||||
read -rsp $'System will now reboot, press any key to continue or Ctrl+C to cancel...\n' -n1 key
|
||||
echo "Rebooting..."
|
||||
reboot
|
||||
else
|
||||
echo "To configure the app, please go to http://$nodeIP:$nodePort"
|
||||
echo "Thank you for joining the network!"
|
||||
echo "Please restart your device to complete the installation"
|
||||
fi
|
||||
|
||||
@@ -17,10 +17,12 @@ echo "<!-- UPDATING ---!>"
|
||||
# Stop any running service
|
||||
systemctl stop RadioNode
|
||||
|
||||
# Update the git Repo
|
||||
installUser=$(cat ./config/installerName)
|
||||
sudo su -l $installUser -c 'git fetch -a -p'
|
||||
sudo su -l $installUser -c 'git pull'
|
||||
# Get the owner of the current working directory
|
||||
cwd_owner=$(stat -c '%U' .)
|
||||
|
||||
# Update the git Repo as the owner of the current working directory
|
||||
sudo su -l $cwd_owner -c 'git fetch'
|
||||
sudo su -l $cwd_owner -c 'git pull'
|
||||
|
||||
# Install any new libraries
|
||||
npm i
|
||||
|
||||
@@ -12,8 +12,8 @@ exports.requestOptions = class requestOptions {
|
||||
if (["POST", "PUT"].includes(method)){
|
||||
log.VERBOSE("Hostname Vars: ", hostname, process.env.SERVER_HOSTNAME, process.env.SERVER_IP);
|
||||
if (hostname) this.hostname = hostname;
|
||||
if (process.env.SERVER_HOSTNAME) this.hostname = process.env.SERVER_HOSTNAME;
|
||||
if (process.env.SERVER_IP) this.hostname = process.env.SERVER_IP;
|
||||
if (!this.hostname && process.env.SERVER_HOSTNAME) this.hostname = process.env.SERVER_HOSTNAME;
|
||||
if (!this.hostname && process.env.SERVER_IP) this.hostname = process.env.SERVER_IP;
|
||||
if (!this.hostname) throw new Error("No server hostname / IP was given when creating a request");
|
||||
this.path = path;
|
||||
this.port = port ?? process.env.SERVER_PORT;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Bootstrap demo</title>
|
||||
<title>'<%=node.name%>' - Configuration</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||
integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.3.0/font/bootstrap-icons.css">
|
||||
@@ -153,7 +153,9 @@
|
||||
</div>
|
||||
|
||||
<% // new System Modal %>
|
||||
<%- include("partials/modifySystemModal.ejs", {'system': "New System" , 'frequencies' : [], 'mode' : '' }) %>
|
||||
<%- include("partials/modifySystemModal.ejs", {'system': "New System" , 'frequencies' : [], 'mode' : '' }) %>
|
||||
<% // Join Server Modal %>
|
||||
<%- include("partials/joinModal.ejs", {'node': node}) %>
|
||||
</body>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"
|
||||
integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<div class="col-md-6">
|
||||
<label class="small mb-1" for="selectRadioPreset">Selected Preset:</label>
|
||||
<select class="custom-select" id="selectRadioPreset">
|
||||
<% for(const system in nearbySystems) { %>
|
||||
<% for(const system in node.nearbySystems) { %>
|
||||
<option value="<%=system%>"><%=system%></option>
|
||||
<% } %>
|
||||
</select>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Modules
|
||||
const { SlashCommandBuilder } = require('discord.js');
|
||||
const { DebugBuilder } = require("../utilities/debugBuilder");
|
||||
const { filterAutocompleteValues } = require("../utilities/utils");
|
||||
const { filterAutocompleteValues, filterPresetsAvailable } = require("../utilities/utils");
|
||||
const { getOnlineNodes, getAllConnections } = require("../utilities/mysqlHandler");
|
||||
const { joinServerWrapper } = require("../controllers/adminController");
|
||||
|
||||
@@ -28,18 +28,7 @@ module.exports = {
|
||||
recordResolve(nodeRows);
|
||||
});
|
||||
});
|
||||
log.DEBUG("Node objects: ", nodeObjects);
|
||||
var presetsAvailable = [];
|
||||
for (const nodeObject of nodeObjects) {
|
||||
log.DEBUG("Node object: ", nodeObject);
|
||||
presetsAvailable.push.apply(presetsAvailable, nodeObject.presets);
|
||||
}
|
||||
|
||||
log.DEBUG("All Presets available: ", presetsAvailable);
|
||||
|
||||
// Remove duplicates
|
||||
options = [...new Set(presetsAvailable)];
|
||||
log.DEBUG("DeDuped Presets available: ", options);
|
||||
const options = await filterPresetsAvailable(nodeObjects);
|
||||
|
||||
// Filter the results to what the user is entering
|
||||
filterAutocompleteValues(interaction, options);
|
||||
|
||||
@@ -82,6 +82,7 @@ a {
|
||||
position: relative;
|
||||
margin-bottom: 30px;
|
||||
box-shadow: 0 0.46875rem 2.1875rem rgba(90, 97, 105, 0.1), 0 0.9375rem 1.40625rem rgba(90, 97, 105, 0.1), 0 0.25rem 0.53125rem rgba(90, 97, 105, 0.12), 0 0.125rem 0.1875rem rgba(90, 97, 105, 0.1);
|
||||
min-height: 85%;
|
||||
}
|
||||
|
||||
.info-card .card-statistic .card-icon-large .bi {
|
||||
|
||||
@@ -30,13 +30,15 @@ function addToastToStorage(time, message) {
|
||||
var toasts = [{ 'time': time, 'message': message }]
|
||||
var storedToasts = getStoredToasts();
|
||||
console.log("Adding new notification to storage: ", toasts);
|
||||
toasts = toasts.concat(storedToasts);
|
||||
console.log("Combined new and stored notifications: ", toasts);
|
||||
toasts = toasts.filter((value, index, self) =>
|
||||
index === self.findIndex((t) => (
|
||||
t.time === value.time && t.message === value.message
|
||||
))
|
||||
)
|
||||
if (storedToasts) {
|
||||
toasts = toasts.concat(storedToasts);
|
||||
console.log("Combined new and stored notifications: ", toasts);
|
||||
toasts = toasts.filter((value, index, self) =>
|
||||
index === self.findIndex((t) => (
|
||||
t.time === value.time && t.message === value.message
|
||||
))
|
||||
)
|
||||
}
|
||||
console.log("Deduped stored notifications: ", toasts);
|
||||
localStorage.setItem("toasts", JSON.stringify(toasts));
|
||||
navbarUpdateNotificationBellCount(toasts);
|
||||
@@ -254,7 +256,7 @@ function joinServer() {
|
||||
console.log(Http.status);
|
||||
console.log(responseObject);
|
||||
createToast(`${responseObject.name} will join shortly`, { showNow: true });
|
||||
$("#joinModal").modal('toggle');
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,32 +1,25 @@
|
||||
var express = require('express');
|
||||
var router = express.Router();
|
||||
|
||||
const { getAllNodes, getNodeInfoFromId } = require("../utilities/mysqlHandler");
|
||||
const { getAllNodes, getNodeInfoFromId, getAllConnections } = require("../utilities/mysqlHandler");
|
||||
const { filterPresetsAvailable } = require("../utilities/utils");
|
||||
|
||||
/* GET home page. */
|
||||
router.get('/', (req, res) => {
|
||||
//var sources = libCore.getSources();
|
||||
return res.render('index');
|
||||
|
||||
var htmlOutput = "";
|
||||
|
||||
sources.forEach(source => {
|
||||
htmlOutput += `
|
||||
<div style='margin-bottom:15px;'>
|
||||
|
||||
<div> Title: ${source.title} </div>
|
||||
<div> Link: ${source.link} </div>
|
||||
<div> category: ${source.category} </div>
|
||||
|
||||
</div>
|
||||
<div>
|
||||
<hr />
|
||||
|
||||
</div>
|
||||
|
||||
`
|
||||
router.get('/', async (req, res) => {
|
||||
var nodes = await new Promise((recordResolve, recordReject) => {
|
||||
getAllNodes((nodeRows) => {
|
||||
recordResolve(nodeRows);
|
||||
});
|
||||
});
|
||||
res.send(htmlOutput);
|
||||
var connections = await getAllConnections();
|
||||
var presets = await new Promise((recordResolve, recordReject) => {
|
||||
getAllNodes((nodeRows) => {
|
||||
recordResolve(filterPresetsAvailable(nodeRows));
|
||||
});
|
||||
});
|
||||
|
||||
//var sources = libCore.getSources();
|
||||
return res.render('index', { 'page': 'index', 'nodes': nodes, 'connections': connections, 'presets': presets });
|
||||
});
|
||||
|
||||
/* GET node controller page. */
|
||||
@@ -35,10 +28,10 @@ router.get('/controller', async (req, res) => {
|
||||
getAllNodes((nodeRows) => {
|
||||
recordResolve(nodeRows);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
//var sources = libCore.getSources();
|
||||
return res.render('controller', {'nodes' : nodes});
|
||||
return res.render('controller', { 'nodes': nodes, 'page': 'controller' });
|
||||
});
|
||||
|
||||
/* GET individual node page. */
|
||||
@@ -46,7 +39,7 @@ router.get('/node/:id', async (req, res) => {
|
||||
var node = await getNodeInfoFromId(req.params.id);
|
||||
|
||||
//var sources = libCore.getSources();
|
||||
return res.render('node', {'node' : node});
|
||||
return res.render('node', { 'node': node, 'page': 'node' });
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@@ -23,7 +23,7 @@ exports.BufferToJson = (buffer) => {
|
||||
* @returns {string} The sanitized preset name to be used elsewhere
|
||||
*/
|
||||
exports.SanitizePresetName = (presetName) => {
|
||||
return String(presetName).toLowerCase().replace(/[\W_]+/g,"-")
|
||||
return String(presetName).toLowerCase().replace(/[\W_]+/g, "-")
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -32,16 +32,16 @@ exports.SanitizePresetName = (presetName) => {
|
||||
* @param interaction Discord interaction object
|
||||
* @param param0.roleName {OPTIONAL} The role name to check the members in; Defaults to 'Bots'
|
||||
*/
|
||||
exports.getMembersInRole = async (interaction, roleName = "Bots" ) => {
|
||||
exports.getMembersInRole = async (interaction, roleName = "Bots") => {
|
||||
log.DEBUG("Fetching all members");
|
||||
var guild = await interaction.client.guilds.fetch({ guild: interaction.guild.id, cache: false }); //cache all members in the server
|
||||
await guild.members.fetch({cache: false});
|
||||
await guild.roles.fetch({cache: false});
|
||||
await guild.members.fetch({ cache: false });
|
||||
await guild.roles.fetch({ cache: false });
|
||||
log.VERBOSE("Guild: ", guild);
|
||||
const role = await guild.roles.cache.find(role => role.name === roleName); //the role to check
|
||||
log.DEBUG("Role to check members from: ", role);
|
||||
log.DEBUG("Members of role: ", role.members);
|
||||
|
||||
|
||||
// This is not working, can't get the status of the users, rest of join is untested
|
||||
const onlineMembers = await role.members.filter(member => member.voice.channel !== null);
|
||||
const offlineMembers = await role.members.filter(member => member.voice.channel === null);
|
||||
@@ -50,8 +50,8 @@ exports.getMembersInRole = async (interaction, roleName = "Bots" ) => {
|
||||
log.VERBOSE("All members: ", allMembers, onlineMembers, offlineMembers)
|
||||
|
||||
return {
|
||||
'online': onlineMembers,
|
||||
'offline': offlineMembers,
|
||||
'online': onlineMembers,
|
||||
'offline': offlineMembers,
|
||||
'all': allMembers
|
||||
}
|
||||
}
|
||||
@@ -64,9 +64,9 @@ exports.getMembersInRole = async (interaction, roleName = "Bots" ) => {
|
||||
*/
|
||||
exports.getKeyByArrayValue = (object, value) => {
|
||||
if (typeof value == "string") return Object.keys(object).find(key => object[key].includes(value));
|
||||
const valueKey = Object.keys(value)[0];
|
||||
const valueKey = Object.keys(value)[0];
|
||||
return Object.keys(object).find(key => (object[key][valueKey] == value[valueKey]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if the input is a valid JSON string
|
||||
@@ -91,7 +91,7 @@ exports.isJsonString = (str) => {
|
||||
exports.getAllClientIds = () => {
|
||||
const jsonClientIds = JSON.parse(readFileSync(path.resolve(__dirname, '../clientIds.json')));
|
||||
var clientObjects = [];
|
||||
for (const jsonClientId of Object.keys(jsonClientIds)){
|
||||
for (const jsonClientId of Object.keys(jsonClientIds)) {
|
||||
clientObjects.push(new clientObject({
|
||||
_discord_id: jsonClientId,
|
||||
_name: jsonClientIds[jsonClientId].name,
|
||||
@@ -110,7 +110,7 @@ exports.getAllClientIds = () => {
|
||||
exports.getClientObjectByClientID = (clientId) => {
|
||||
const clientObjects = this.getAllClientIds();
|
||||
log.DEBUG("All client IDs: ", clientObjects);
|
||||
for (const clientObject of clientObjects){
|
||||
for (const clientObject of clientObjects) {
|
||||
if (clientObject.clientId == clientId) {
|
||||
log.DEBUG("Found client ID from given ID: ", clientObject);
|
||||
return clientObject
|
||||
@@ -138,4 +138,26 @@ exports.filterAutocompleteValues = async (interaction, options) => {
|
||||
await interaction.respond(
|
||||
filtered.map(option => ({ name: option, value: option })),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter an array of nodeObjects to get all unique presets within
|
||||
*
|
||||
* @param {Array} nodeObjects An array of nodeObjects to get the presets from
|
||||
* @returns {Array} Presets available from given nodeObjects
|
||||
*/
|
||||
exports.filterPresetsAvailable = async (nodeObjects) => {
|
||||
log.DEBUG("Node objects: ", nodeObjects);
|
||||
var presetsAvailable = [];
|
||||
for (const nodeObject of nodeObjects) {
|
||||
log.DEBUG("Node object: ", nodeObject);
|
||||
presetsAvailable.push.apply(presetsAvailable, nodeObject.presets);
|
||||
}
|
||||
|
||||
log.DEBUG("All Presets available: ", presetsAvailable);
|
||||
|
||||
// Remove duplicates
|
||||
presetsAvailable = [...new Set(presetsAvailable)];
|
||||
log.DEBUG("DeDuped Presets available: ", presetsAvailable);
|
||||
return presetsAvailable;
|
||||
}
|
||||
@@ -1,133 +1,64 @@
|
||||
<%- include('partials/htmlHead.ejs') %>
|
||||
<%- include('partials/htmlHead.ejs', {'page': page}) %>
|
||||
<div class="container">
|
||||
<div class="row row-cols-1 row-cols-md-2 row-cols-xl-4 mt-2">
|
||||
<%- include('partials/valueChip.ejs', {
|
||||
'title': 'Nodes in the Network',
|
||||
'bgColor': "orange-dark",
|
||||
'value': nodes.length,
|
||||
'progressPercent': false,
|
||||
'icon': 'server'
|
||||
}) %>
|
||||
|
||||
<%- include('partials/valueChip.ejs', {
|
||||
'title': 'Nodes Online',
|
||||
'bgColor': "green",
|
||||
'value': nodes.filter(node => node.online).length,
|
||||
'progressPercent': false,
|
||||
'icon': 'cpu-fill'
|
||||
}) %>
|
||||
|
||||
<%- include('partials/valueChip.ejs', {
|
||||
'title': 'Nodes with Discord Connections',
|
||||
'bgColor': "blue-dark",
|
||||
'value': connections.length,
|
||||
'progressPercent': false,
|
||||
'icon': 'gear-wide-connected'
|
||||
}) %>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="my-4">
|
||||
<p><h3><b>Current Connections</b></h3></p>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="row row-cols-1 row-cols-md-2 row-cols-xl-4">
|
||||
<div class="col-xl-3 col-lg-6">
|
||||
<div class="info-card l-bg-cherry">
|
||||
<div class="card-statistic p-4">
|
||||
<div class="card-icon card-icon-large"><i class="bi bi-cart"></i></div>
|
||||
<div class="mb-4">
|
||||
<h5 class="card-title mb-0">New Orders</h5>
|
||||
</div>
|
||||
<div class="row align-items-center mb-2 d-flex">
|
||||
<div class="col-8">
|
||||
<h2 class="d-flex align-items-center mb-0">
|
||||
3,243
|
||||
</h2>
|
||||
</div>
|
||||
<div class="col-4 text-right">
|
||||
<span>12.5% <i class="fa fa-arrow-up"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress mt-1 " data-height="8" style="height: 8px;">
|
||||
<div class="progress-bar l-bg-cyan" role="progressbar" data-width="25%" aria-valuenow="25"
|
||||
aria-valuemin="0" aria-valuemax="100" style="width: 25%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% for(const conn of connections) { %>
|
||||
<%- include('partials/connectionCard.ejs', {'connection': conn}) %>
|
||||
<%}%>
|
||||
</div>
|
||||
|
||||
<div class="col-xl-3 col-lg-6">
|
||||
<div class="info-card l-bg-blue-dark">
|
||||
<div class="card-statistic p-4">
|
||||
<div class="card-icon card-icon-large"><i class="bi bi-cart"></i></div>
|
||||
<div class="mb-4">
|
||||
<h5 class="card-title mb-0">New Orders</h5>
|
||||
</div>
|
||||
<div class="row align-items-center mb-2 d-flex">
|
||||
<div class="col-8">
|
||||
<h2 class="d-flex align-items-center mb-0">
|
||||
3,243
|
||||
</h2>
|
||||
</div>
|
||||
<div class="col-4 text-right">
|
||||
<span>12.5% <i class="fa fa-arrow-up"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress mt-1 " data-height="8" style="height: 8px;">
|
||||
<div class="progress-bar l-bg-cyan" role="progressbar" data-width="25%" aria-valuenow="25"
|
||||
aria-valuemin="0" aria-valuemax="100" style="width: 25%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xl-3 col-sm-6">
|
||||
<div class="card node-card">
|
||||
<div class="card-body">
|
||||
<div class="dropdown float-end">
|
||||
<a class="text-muted dropdown-toggle font-size-16" href="#" role="button"
|
||||
data-bs-toggle="dropdown" aria-haspopup="true"><i
|
||||
class="bx bx-dots-horizontal-rounded"></i></a>
|
||||
<div class="dropdown-menu dropdown-menu-end"><a class="dropdown-item" href="#">Edit</a><a
|
||||
class="dropdown-item" href="#">Action</a><a class="dropdown-item"
|
||||
href="#">Remove</a></div>
|
||||
</div>
|
||||
<div class="d-flex align-items-center">
|
||||
<div><img src="https://bootdey.com/img/Content/avatar/avatar1.png" alt=""
|
||||
class="avatar-md rounded-circle img-thumbnail" /></div>
|
||||
<div class="flex-1 ms-3">
|
||||
<h5 class="font-size-16 mb-1"><a href="#" class="text-dark">Phyllis Gatlin</a></h5>
|
||||
<span class="badge badge-soft-success mb-0">Full Stack Developer</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 pt-1">
|
||||
<p class="text-muted mb-0"><i
|
||||
class="mdi mdi-phone font-size-15 align-middle pe-2 text-primary"></i> 070 2860 5375
|
||||
</p>
|
||||
<p class="text-muted mb-0 mt-2"><i
|
||||
class="mdi mdi-email font-size-15 align-middle pe-2 text-primary"></i>
|
||||
PhyllisGatlin@spy.com</p>
|
||||
<p class="text-muted mb-0 mt-2"><i
|
||||
class="mdi mdi-google-maps font-size-15 align-middle pe-2 text-primary"></i> 52
|
||||
Ilchester MYBSTER 9WX</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xl-3 col-sm-6">
|
||||
<div class="card node-card">
|
||||
<div class="card-body">
|
||||
<div class="dropdown float-end">
|
||||
<a class="text-muted dropdown-toggle font-size-16" href="#" role="button"
|
||||
data-bs-toggle="dropdown" aria-haspopup="true"><i
|
||||
class="bx bx-dots-horizontal-rounded"></i></a>
|
||||
<div class="dropdown-menu dropdown-menu-end"><a class="dropdown-item" href="#">Edit</a><a
|
||||
class="dropdown-item" href="#">Action</a><a class="dropdown-item"
|
||||
href="#">Remove</a></div>
|
||||
</div>
|
||||
<div class="d-flex align-items-center">
|
||||
<div><img src="https://bootdey.com/img/Content/avatar/avatar5.png" alt=""
|
||||
class="avatar-md rounded-circle img-thumbnail" /></div>
|
||||
<div class="flex-1 ms-3">
|
||||
<h5 class="font-size-16 mb-1"><a href="#" class="text-dark">Diana Owens</a></h5>
|
||||
<span class="badge badge-soft-danger mb-0">UI/UX Designer</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 pt-1">
|
||||
<p class="text-muted mb-0"><i
|
||||
class="mdi mdi-phone font-size-15 align-middle pe-2 text-primary"></i> 087 6321 3235
|
||||
</p>
|
||||
<p class="text-muted mb-0 mt-2"><i
|
||||
class="mdi mdi-email font-size-15 align-middle pe-2 text-primary"></i>
|
||||
DianaOwens@spy.com</p>
|
||||
<p class="text-muted mb-0 mt-2"><i
|
||||
class="mdi mdi-google-maps font-size-15 align-middle pe-2 text-primary"></i> 52
|
||||
Ilchester MYBSTER 9WX</p>
|
||||
</div>
|
||||
<div class="d-flex gap-2 pt-4">
|
||||
<button type="button" class="btn btn-soft-primary btn-sm w-50"><i
|
||||
class="bx bx-user me-1"></i> Profile</button>
|
||||
<button type="button" class="btn btn-primary btn-sm w-50"><i
|
||||
class="bx bx-message-square-dots me-1"></i> Contact</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="my-4">
|
||||
<p><h3><b>Online Nodes</b></h3></p>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="row row-cols-1 row-cols-md-2 row-cols-xl-4">
|
||||
<% for(const node of nodes.filter(node => node.online)) { %>
|
||||
<%- include('partials/nodeCard.ejs', {'node': node}) %>
|
||||
<%}%>
|
||||
</div>
|
||||
<div class="my-4">
|
||||
<p><h3><b>Offline Nodes</b></h3></p>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="row row-cols-1 row-cols-md-2 row-cols-xl-4">
|
||||
<% for(const node of nodes.filter(node => node.online == false)) { %>
|
||||
<%- include('partials/nodeCard.ejs', {'node': node}) %>
|
||||
<%}%>
|
||||
</div>
|
||||
</div>
|
||||
<%- include('partials/bodyEnd.ejs') %>
|
||||
|
||||
<%- include('partials/htmlFooter.ejs') %>
|
||||
@@ -134,5 +134,7 @@
|
||||
|
||||
<% // new System Modal %>
|
||||
<%- include("partials/modifySystemModal.ejs", {'system': "New System", 'frequencies': [], 'mode': ''}) %>
|
||||
<% // Join Server Modal %>
|
||||
<%- include("partials/joinModal.ejs", {'node': node}) %>
|
||||
<%- include('partials/bodyEnd.ejs') %>
|
||||
<%- include('partials/htmlFooter.ejs') %>
|
||||
29
Server/views/partials/connectioncard.ejs
Normal file
29
Server/views/partials/connectioncard.ejs
Normal file
@@ -0,0 +1,29 @@
|
||||
<div class="col-xl-3 col-sm-6">
|
||||
<div class="card node-card">
|
||||
<div class="card-body">
|
||||
<div class="dropdown float-end">
|
||||
<a class="text-muted dropdown-toggle font-size-16" href="#" role="button" data-bs-toggle="dropdown"
|
||||
aria-haspopup="true">
|
||||
<i class="bx bx-dots-horizontal-rounded"></i>
|
||||
</a>
|
||||
<div class="dropdown-menu dropdown-menu-end">
|
||||
<a class="dropdown-item node-action">Edit</a>
|
||||
<a class="dropdown-item node-action" onclick="">Send Heartbeat</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="flex-1 ms-3">
|
||||
<h5 class="font-size-16 mb-1"><a class="text-dark">
|
||||
<%= connection.clientObject.name%>
|
||||
</a></h5>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 pt-1">
|
||||
<p class="text-muted mb-0">
|
||||
<i class="bi bi-cpu-fill font-size-15 align-middle pe-2 text-primary"></i>
|
||||
Node ID: <a href="/node/<%= connection.node.id %>"><%= connection.node.id %></a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,7 +1,20 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Bootstrap demo</title>
|
||||
<% switch (page) {
|
||||
case "index":%>
|
||||
<title>Node Dashboard</title>
|
||||
<% break;
|
||||
case "controller":%>
|
||||
<title>Node Controller</title>
|
||||
<% break;
|
||||
case "node":%>
|
||||
<title>Node Configuration</title>
|
||||
<% break;
|
||||
default:%>
|
||||
<title>DRB_CnC Server</title>
|
||||
<%break;
|
||||
} %>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||
integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.3.0/font/bootstrap-icons.css">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!doctype html>
|
||||
<html lang="en" data-bs-theme="auto">
|
||||
<%- include('head.ejs') %>
|
||||
<%- include('head.ejs', {'page': page}) %>
|
||||
<body>
|
||||
<%- include('navbar.ejs') %>
|
||||
<%- include('sidebar.ejs') %>
|
||||
@@ -24,7 +24,7 @@
|
||||
<div class="col-md-6">
|
||||
<label class="small mb-1" for="selectRadioPreset">Selected Preset:</label>
|
||||
<select class="custom-select" id="selectRadioPreset">
|
||||
<% for(const system in nearbySystems) { %>
|
||||
<% for(const system in node.nearbySystems) { %>
|
||||
<option value="<%=system%>"><%=system%></option>
|
||||
<% } %>
|
||||
</select>
|
||||
|
||||
32
Server/views/partials/valueChip.ejs
Normal file
32
Server/views/partials/valueChip.ejs
Normal file
@@ -0,0 +1,32 @@
|
||||
<div class="col-xl-3 col-lg-6">
|
||||
<div class="info-card l-bg-<%=bgColor%>">
|
||||
<div class="card-statistic p-4">
|
||||
<div class="card-icon card-icon-large"><i class="bi bi-<%=icon%>"></i></div>
|
||||
<div class="mb-4">
|
||||
<h5 class="card-title mb-0">
|
||||
<%=title%>
|
||||
</h5>
|
||||
</div>
|
||||
<div class="row align-items-center mb-2 d-flex">
|
||||
<div class="col-8">
|
||||
<h2 class="d-flex align-items-center mb-0">
|
||||
<%=value%>
|
||||
</h2>
|
||||
</div>
|
||||
<% if (progressPercent) {%>
|
||||
<div class="col-4 text-right">
|
||||
<span>
|
||||
<%=progressPercent%>%<i class="fa fa-arrow-up"></i>
|
||||
</span>
|
||||
</div>
|
||||
<%}%>
|
||||
</div>
|
||||
<% if (progressPercent) {%>
|
||||
<div class="progress mt-1 " data-height="8" style="height: 8px;">
|
||||
<div class="progress-bar l-bg-cyan" role="progressbar" data-width="<%=progressPercent%>%" aria-valuenow="<%=progressPercent%>"
|
||||
aria-valuemin="0" aria-valuemax="100" style="width: <%=progressPercent%>%;"></div>
|
||||
</div>
|
||||
<%}%>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user