Browse Source

semi-working container graphs

It's pretty buggy, but it works.
lllllllillllllillll 1 year ago
parent
commit
c063a5a0e8
8 changed files with 165 additions and 109 deletions
  1. 10 8
      app.js
  2. 2 1
      components/appCard.js
  3. 1 1
      components/dashCard.js
  4. 100 83
      controllers/app_actions.js
  5. 2 1
      package.json
  6. 47 12
      public/js/main.js
  7. 1 1
      templates.json
  8. 2 2
      views/partials/navbar.ejs

+ 10 - 8
app.js

@@ -112,11 +112,6 @@ io.on('connection', (socket) => {
                     volumes.push(`${value.Source}: ${value.Destination}: ${value.RW}`);
                 }
 
-                console.log(volumes[0])
-                console.log(volumes[1])
-                console.log(volumes[2])
-
-
                 // console.log('Environment Variables:');
                 // for (const [key, value] of Object.entries(containerInfo.Config.Env)) {
                 //     console.log(`${key}: ${value}`);
@@ -127,13 +122,20 @@ io.on('connection', (socket) => {
                 //     console.log(`${key}: ${value}`);
                 // }
 
-                // dockerContainerStats(container.Id).then((data) => {
-                //     console.log(`${container.Names[0].slice(1)} // CPU: ${Math.round(data[0].cpuPercent)} // RAM: ${Math.round(data[0].memPercent)}`);
-                // });
+                dockerContainerStats(container.Id).then((data) => {
+                    let container_stats = {
+                        name: container.Names[0].slice(1),
+                        cpu: Math.round(data[0].cpuPercent),
+                        ram: Math.round(data[0].memPercent)
+                    }
+                    console.log(container_stats);
+                    socket.emit('container_stats', container_stats);
+                });
                 
                 let dockerCard = dashCard(container.Names[0].slice(1), service, container.Id, container.State, container.Image, external_port, internal_port);
                 // open_ports += `-L ${external_port}:localhost:${external_port} `
                 card_list += dockerCard;
+                
             }
 
             // emit card list is it's different from what was sent last time, then clear install local

+ 2 - 1
components/appCard.js

@@ -15,6 +15,7 @@ function appCard(data) {
   let repository = data.repository || "";
   let source = data.image || "";
 
+
   // if data.network is set to host, bridge, or docker set the radio button to checked
   let net_host, net_bridge, net_docker = '';
   let net_name = 'AppBridge';
@@ -38,7 +39,7 @@ function appCard(data) {
 
 
   if (repository != "") {
-    source = `${repository.url}/raw/master/${repository.stackfile}`;
+    source = (`${repository.url}/raw/master/${repository.stackfile}`);
   }
 
 

+ 1 - 1
components/dashCard.js

@@ -150,7 +150,7 @@ function dashCard(name, service, id, state, image, external_port, internal_port)
               </span>
             </div>
           </div>
-          <div id="cardChart" class="chart-sm"></div>
+          <div id="${name}_chart" class="chart-sm"></div>
         </div>
       </div>
     </div>

+ 100 - 83
controllers/app_actions.js

@@ -1,6 +1,7 @@
-const { writeFileSync, mkdirSync, appendFileSync } = require("fs");
-const { exec } = require("child_process");
+const { writeFileSync, mkdirSync, readFileSync } = require("fs");
+const { exec, execSync } = require("child_process");
 const { dashCard } = require('../components/dashCard');
+const yaml = require('js-yaml');
 
 
 
@@ -8,7 +9,6 @@ exports.Install = async function (req, res) {
     
     if (req.session.role == "admin") {
 
-        
 
         let { service_name, name, image, command_check, command, net_mode, restart_policy } = req.body;
 
@@ -18,111 +18,128 @@ exports.Install = async function (req, res) {
         let { label0, label1, label2, label3, label4, label5, label6, label7, label8, label9, label10, label11 } = req.body;
 
 
-        let installCard = dashCard(req.body.name, req.body.service_name, '', 'installing', req.body.image, 0, 0);
-        req.app.locals.install = installCard;
+        if (image.startsWith('https://')){
+            mkdirSync(`./appdata/${name}`, { recursive: true });
+            execSync(`curl -o ./appdata/${name}/${name}_stack.yml -L ${image}`);
+            console.log(`Downloaded stackfile: ${image}`);
+            let stackfile = yaml.load(readFileSync(`./appdata/${name}/${name}_stack.yml`, 'utf8'));
+            let services = Object.keys(stackfile.services);
+
+            for ( let i = 0; i < services.length; i++ ) {
+                try {
+                    console.log(stackfile.services[Object.keys(stackfile.services)[i]].environment);
+                } catch { console.log('no env') }
+            }
+            
+        } else {
 
-        let compose_file = `version: '3'`;
-        compose_file += `\nservices:`
-        compose_file += `\n  ${service_name}:`
-        compose_file += `\n    container_name: ${name}`;
-        compose_file += `\n    image: ${image}`;
 
-        // Command
-        if (command_check == 'on') {
-            compose_file += `\n    command: ${command}`
-        }
+            let installCard = dashCard(req.body.name, req.body.service_name, '', 'installing', req.body.image, 0, 0);
+            req.app.locals.install = installCard;
 
-        // Network mode
-        if (net_mode == 'host') {
-            compose_file += `\n    network_mode: 'host'`
-        }
-        else if (net_mode != 'host' && net_mode != 'docker') {
-            compose_file += `\n    network_mode: '${net_mode}'`
-        }
-        
-        // Restart policy
-        if (restart_policy != '') {
-            compose_file += `\n    restart: ${restart_policy}`
-        }
+            let compose_file = `version: '3'`;
+            compose_file += `\nservices:`
+            compose_file += `\n  ${service_name}:`
+            compose_file += `\n    container_name: ${name}`;
+            compose_file += `\n    image: ${image}`;
 
-        // Ports
-        if ((port0 == 'on' || port1 == 'on' || port2 == 'on' || port3 == 'on' || port4 == 'on' || port5 == 'on') && (net_mode != 'host')) {
-            compose_file += `\n    ports:`
+            // Command
+            if (command_check == 'on') {
+                compose_file += `\n    command: ${command}`
+            }
 
-                for (let i = 0; i < 6; i++) {
-                    if (req.body[`port${i}`] == 'on') {
-                        compose_file += `\n      - ${req.body[`port_${i}_external`]}:${req.body[`port_${i}_internal`]}/${req.body[`port_${i}_protocol`]}`
+            // Network mode
+            if (net_mode == 'host') {
+                compose_file += `\n    network_mode: 'host'`
+            }
+            else if (net_mode != 'host' && net_mode != 'docker') {
+                compose_file += `\n    network_mode: '${net_mode}'`
+            }
+            
+            // Restart policy
+            if (restart_policy != '') {
+                compose_file += `\n    restart: ${restart_policy}`
+            }
+
+            // Ports
+            if ((port0 == 'on' || port1 == 'on' || port2 == 'on' || port3 == 'on' || port4 == 'on' || port5 == 'on') && (net_mode != 'host')) {
+                compose_file += `\n    ports:`
+
+                    for (let i = 0; i < 6; i++) {
+                        if (req.body[`port${i}`] == 'on') {
+                            compose_file += `\n      - ${req.body[`port_${i}_external`]}:${req.body[`port_${i}_internal`]}/${req.body[`port_${i}_protocol`]}`
+                        }
                     }
-                }
-        }
+            }
 
-        // Volumes
-        if (volume0 == 'on' || volume1 == 'on' || volume2 == 'on' || volume3 == 'on' || volume4 == 'on' || volume5 == 'on') {
-            compose_file += `\n    volumes:`
+            // Volumes
+            if (volume0 == 'on' || volume1 == 'on' || volume2 == 'on' || volume3 == 'on' || volume4 == 'on' || volume5 == 'on') {
+                compose_file += `\n    volumes:`
 
-            for (let i = 0; i < 6; i++) {
-                if (req.body[`volume${i}`] == 'on') {
-                    compose_file += `\n      - ${req.body[`volume_${i}_bind`]}:${req.body[`volume_${i}_container`]}:${req.body[`volume_${i}_readwrite`]}`
+                for (let i = 0; i < 6; i++) {
+                    if (req.body[`volume${i}`] == 'on') {
+                        compose_file += `\n      - ${req.body[`volume_${i}_bind`]}:${req.body[`volume_${i}_container`]}:${req.body[`volume_${i}_readwrite`]}`
+                    }
                 }
             }
-        }
 
-        // Environment variables
-        if (env0 == 'on' || env1 == 'on' || env2 == 'on' || env3 == 'on' || env4 == 'on' || env5 == 'on' || env6 == 'on' || env7 == 'on' || env8 == 'on' || env9 == 'on' || env10 == 'on' || env11 == 'on') {
-            compose_file += `\n    environment:`
-        }
-        for (let i = 0; i < 12; i++) {
-            if (req.body[`env${i}`] == 'on') {
-                compose_file += `\n      - ${req.body[`env_${i}_name`]}=${req.body[`env_${i}_default`]}`
+            // Environment variables
+            if (env0 == 'on' || env1 == 'on' || env2 == 'on' || env3 == 'on' || env4 == 'on' || env5 == 'on' || env6 == 'on' || env7 == 'on' || env8 == 'on' || env9 == 'on' || env10 == 'on' || env11 == 'on') {
+                compose_file += `\n    environment:`
+            }
+            for (let i = 0; i < 12; i++) {
+                if (req.body[`env${i}`] == 'on') {
+                    compose_file += `\n      - ${req.body[`env_${i}_name`]}=${req.body[`env_${i}_default`]}`
 
+                }
             }
-        }
 
-        // Add labels
-        if (label0 == 'on' || label1 == 'on' || label2 == 'on' || label3 == 'on' || label4 == 'on' || label5 == 'on' || label6 == 'on' || label7 == 'on' || label8 == 'on' || label9 == 'on' || label10 == 'on' || label11 == 'on') {
-            compose_file += `\n    labels:`
-        }   
-        for (let i = 0; i < 12; i++) {
-            if (req.body[`label${i}`] == 'on') {
-                compose_file += `\n      - ${req.body[`label_${i}_name`]}=${req.body[`label_${i}_value`]}`
+            // Add labels
+            if (label0 == 'on' || label1 == 'on' || label2 == 'on' || label3 == 'on' || label4 == 'on' || label5 == 'on' || label6 == 'on' || label7 == 'on' || label8 == 'on' || label9 == 'on' || label10 == 'on' || label11 == 'on') {
+                compose_file += `\n    labels:`
+            }   
+            for (let i = 0; i < 12; i++) {
+                if (req.body[`label${i}`] == 'on') {
+                    compose_file += `\n      - ${req.body[`label_${i}_name`]}=${req.body[`label_${i}_value`]}`
+                }
             }
-        }
 
-        // Add privileged mode 
+            // Add privileged mode 
 
-        if (req.body.privileged == 'on') {
-            compose_file += `\n    privileged: true`
-        }
+            if (req.body.privileged == 'on') {
+                compose_file += `\n    privileged: true`
+            }
 
 
-        // Add hardware acceleration to the docker-compose file if one of the environment variables has the label DRINODE
-        if (env0 == 'on' || env1 == 'on' || env2 == 'on' || env3 == 'on' || env4 == 'on' || env5 == 'on' || env6 == 'on' || env7 == 'on' || env8 == 'on' || env9 == 'on' || env10 == 'on' || env11 == 'on') {
-            for (let i = 0; i < 12; i++) {
-                if (req.body[`env${i}`] == 'on') {
-                    if (req.body[`env_${i}_name`] == 'DRINODE') {
-                        compose_file += `\n    deploy:`
-                        compose_file += `\n      resources:`
-                        compose_file += `\n        reservations:`
-                        compose_file += `\n          devices:`
-                        compose_file += `\n          - driver: nvidia`
-                        compose_file += `\n            count: 1`
-                        compose_file += `\n            capabilities: [gpu]`
+            // Add hardware acceleration to the docker-compose file if one of the environment variables has the label DRINODE
+            if (env0 == 'on' || env1 == 'on' || env2 == 'on' || env3 == 'on' || env4 == 'on' || env5 == 'on' || env6 == 'on' || env7 == 'on' || env8 == 'on' || env9 == 'on' || env10 == 'on' || env11 == 'on') {
+                for (let i = 0; i < 12; i++) {
+                    if (req.body[`env${i}`] == 'on') {
+                        if (req.body[`env_${i}_name`] == 'DRINODE') {
+                            compose_file += `\n    deploy:`
+                            compose_file += `\n      resources:`
+                            compose_file += `\n        reservations:`
+                            compose_file += `\n          devices:`
+                            compose_file += `\n          - driver: nvidia`
+                            compose_file += `\n            count: 1`
+                            compose_file += `\n            capabilities: [gpu]`
+                        }
                     }
                 }
             }
-        }
 
-        try {   
-            mkdirSync(`./appdata/${name}`, { recursive: true });
-            writeFileSync(`./appdata/${name}/docker-compose.yml`, compose_file, function (err) { console.log(err) });
+            try {   
+                mkdirSync(`./appdata/${name}`, { recursive: true });
+                writeFileSync(`./appdata/${name}/docker-compose.yml`, compose_file, function (err) { console.log(err) });
 
-            exec(`docker compose -f ./appdata/${name}/docker-compose.yml up -d`, (error, stdout, stderr) => {
-                if (error) { console.error(`error: ${error.message}`); return; }
-                if (stderr) { console.error(`stderr: ${stderr}`); return; }
-                console.log(`stdout:\n${stdout}`);
-            });
-        } catch { console.log('error creating directory or compose file') }
+                exec(`docker compose -f ./appdata/${name}/docker-compose.yml up -d`, (error, stdout, stderr) => {
+                    if (error) { console.error(`error: ${error.message}`); return; }
+                    if (stderr) { console.error(`stderr: ${stderr}`); return; }
+                    console.log(`stdout:\n${stdout}`);
+                });
+            } catch { console.log('error creating directory or compose file') }
 
+        }
 
         // Redirect to the home page
         res.redirect("/");

+ 2 - 1
package.json

@@ -17,7 +17,8 @@
     "sequelize": "^6.32.1",
     "socket.io": "^4.6.1",
     "sqlite3": "^5.1.6",
-    "systeminformation": "^5.17.12"
+    "systeminformation": "^5.17.12",
+    "js-yaml": "^4.1.0"
   },
   "description": ""
 }

+ 47 - 12
public/js/main.js

@@ -22,6 +22,8 @@ const diskBar = document.getElementById('disk-bar');
 
 const dockerCards = document.getElementById('cards');
 
+// create
+
 //Update usage bars
 socket.on('metrics', ({ cpu, ram, tx, rx, disk}) => {
     cpuText.innerHTML = `<span>CPU ${cpu} %</span>`;
@@ -32,8 +34,17 @@ socket.on('metrics', ({ cpu, ram, tx, rx, disk}) => {
     diskBar.innerHTML = `<span style="width: ${disk}%"><span></span></span>`;
 });
 
-function drawCharts() {
-  var elements = document.querySelectorAll("#cardChart");
+function drawCharts(name, cpu_array, ram_array) {
+  var elements = document.querySelectorAll(`${name}`);
+
+  if (cpu_array == undefined) {
+    cpu_array = [37, 35, 44, 28, 36, 24, 65, 31, 37, 39, 62, 51, 35, 41, 35, 27, 93, 53, 61, 27, 54, 43, 4, 46, 39, 62, 51, 35, 41, 67];
+  }
+
+  if (ram_array == undefined) {
+    ram_array = [93, 54, 51, 24, 35, 35, 31, 67, 19, 43, 28, 36, 62, 61, 27, 39, 35, 41, 27, 35, 51, 46, 62, 37, 44, 53, 41, 65, 39, 37];
+  }
+
 
   Array.from(elements).forEach(function(element) {
     if (window.ApexCharts) {
@@ -60,10 +71,10 @@ function drawCharts() {
         },
         series: [{
           name: "CPU",
-          data: [37, 35, 44, 28, 36, 24, 65, 31, 37, 39, 62, 51, 35, 41, 35, 27, 93, 53, 61, 27, 54, 43, 4, 46, 39, 62, 51, 35, 41, 67]
+          data: cpu_array
         }, {
           name: "RAM",
-          data: [93, 54, 51, 24, 35, 35, 31, 67, 19, 43, 28, 36, 62, 61, 27, 39, 35, 41, 27, 35, 51, 46, 62, 37, 44, 53, 41, 65, 39, 37]
+          data: ram_array
         }],
         tooltip: {
           theme: 'dark'
@@ -97,14 +108,8 @@ function drawCharts() {
   });
 }
 
+// container button actions
 function buttonAction(button) {
-
-// if the button name is 'CaddyProxyManager' and the value is 'install' grab the div with the id of 'sites' and remove d-none class. also add the d-none class to the div with the id of 'CaddyInstallCard'
-  if (button.name == 'CaddyProxyManager' && button.value == 'install') {
-    document.getElementById('sites').classList.remove('d-none');
-    document.getElementById('CaddyInstallCard').classList.add('d-none');
-  }
-
   socket.emit('clicked', {container: button.name, state: button.id, action: button.value});
 }
 
@@ -117,10 +122,40 @@ socket.on('cards', (data) => {
   });
  
   dockerCards.insertAdjacentHTML("afterend", data);
-  drawCharts();
+  // drawCharts('#cardChart');
 });
 
 
+socket.on('container_stats', (data) => {
+
+  let {name, cpu, ram} = data;
+
+  // get cpu and ram array of the container from local storage
+  var cpu_array = JSON.parse(localStorage.getItem(`${name}_cpu`));
+  var ram_array = JSON.parse(localStorage.getItem(`${name}_ram`));
+  
+  // if the cpu and ram arrays are null, create both arrays with 30 values of 0
+  if (cpu_array == null) { cpu_array = Array(30).fill(0); }
+  if (ram_array == null) { ram_array = Array(30).fill(0); }
+
+  // add the new cpu and ram values to the arrays, but limit the array to 30 values
+  cpu_array.push(cpu);
+  ram_array.push(ram);
+  cpu_array = cpu_array.slice(-30);
+  ram_array = ram_array.slice(-30);
+
+  // save the arrays to local storage
+  localStorage.setItem(`${name}_cpu`, JSON.stringify(cpu_array));
+  localStorage.setItem(`${name}_ram`, JSON.stringify(ram_array));
+
+  // replace the old chart with the new one without breaking the surrounding elements
+  let chart = document.getElementById(`${name}_chart`);
+  let newChart = document.createElement('div');
+  newChart.id = `${name}_chart`;
+  chart.parentNode.replaceChild(newChart, chart);
+  drawCharts(`#${name}_chart`, cpu_array, ram_array);  
+});
+
 socket.on('install', (data) => {
   
   console.log('added install card');

+ 1 - 1
templates.json

@@ -3016,7 +3016,7 @@
           "name": "PORT"
         }
       ],
-      "logo": "https://raw.githubusercontent.com/walkxcode/dashboard-icons/main/png/authentik.png",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/authentik.png",
       "name": "authentik",
       "platform": "linux",
       "repository": {

+ 2 - 2
views/partials/navbar.ejs

@@ -59,7 +59,7 @@
     <div class="navbar-nav flex-row order-md-last">
       <div class="nav-item d-none d-md-flex me-3">
         <div class="btn-list">
-          <a href="#" class="btn text-white">
+          <a href="#" class="btn text-black">
             <!-- Download SVG icon from http://tabler-icons.io/i/lock -->
             <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-lock" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M5 13a2 2 0 0 1 2 -2h10a2 2 0 0 1 2 2v6a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2 -2v-6z"></path> <path d="M11 16a1 1 0 1 0 2 0a1 1 0 0 0 -2 0"></path> <path d="M8 11v-4a4 4 0 1 1 8 0v4"></path> </svg>
             VPN
@@ -69,7 +69,7 @@
             <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-shield" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M12 3a12 12 0 0 0 8.5 3a12 12 0 0 1 -8.5 15a12 12 0 0 1 -8.5 -15a12 12 0 0 0 8.5 -3"></path> </svg>
             Firewall
           </a>
-          <a href="#" class="btn text-white">
+          <a href="#" class="btn text-black">
             <!-- Download SVG icon from http://tabler-icons.io/i/shield -->
             <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-screen-share" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M21 12v3a1 1 0 0 1 -1 1h-16a1 1 0 0 1 -1 -1v-10a1 1 0 0 1 1 -1h9"></path> <path d="M7 20l10 0"></path> <path d="M9 16l0 4"></path> <path d="M15 16l0 4"></path> <path d="M17 4h4v4"></path> <path d="M16 9l5 -5"></path> </svg>
             VNC