Browse Source

Fixed hide/resetView. Renamed app.js to server.js.
Added install and uninstall to Syslog.

lllllllillllllillll 1 year ago
parent
commit
3fac67621a
8 changed files with 437 additions and 381 deletions
  1. 1 1
      Dockerfile
  2. 1 1
      controllers/images.js
  3. 1 1
      controllers/networks.js
  4. 1 1
      controllers/volumes.js
  5. 62 41
      functions/install.js
  6. 13 2
      functions/uninstall.js
  7. 1 1
      package.json
  8. 357 333
      server.js

+ 1 - 1
Dockerfile

@@ -19,4 +19,4 @@ COPY . .
 
 
 EXPOSE 8000
 EXPOSE 8000
 
 
-CMD ["pm2-runtime", "app.js"]
+CMD ["pm2-runtime", "server.js"]

+ 1 - 1
controllers/images.js

@@ -1,4 +1,4 @@
-import { docker } from '../app.js';
+import { docker } from '../server.js';
 
 
 export const Images = async function(req, res) {
 export const Images = async function(req, res) {
 
 

+ 1 - 1
controllers/networks.js

@@ -1,4 +1,4 @@
-import { docker } from '../app.js';
+import { docker } from '../server.js';
 
 
 
 
 export const Networks = async function(req, res) {
 export const Networks = async function(req, res) {

+ 1 - 1
controllers/volumes.js

@@ -1,4 +1,4 @@
-import { docker } from '../app.js';
+import { docker } from '../server.js';
 
 
 
 
 export const Volumes = async function(req, res) {
 export const Volumes = async function(req, res) {

+ 62 - 41
functions/install.js

@@ -1,11 +1,12 @@
 import { writeFileSync, mkdirSync, readFileSync } from "fs";
 import { writeFileSync, mkdirSync, readFileSync } from "fs";
 import yaml from 'js-yaml';
 import yaml from 'js-yaml';
 import { execSync } from "child_process";
 import { execSync } from "child_process";
-import { docker } from "../app.js";
+import { docker } from "../server.js";
 import DockerodeCompose from "dockerode-compose";
 import DockerodeCompose from "dockerode-compose";
+import { Syslog } from "../database/models.js";
 
 
 
 
-
+// This entire page hurts to look at. 
 export const Install = async (req, res) => {
 export const Install = async (req, res) => {
 
 
         let data = req.body;
         let data = req.body;
@@ -32,7 +33,6 @@ export const Install = async (req, res) => {
                     console.log(stackfile.services[Object.keys(stackfile.services)[i]].environment);
                     console.log(stackfile.services[Object.keys(stackfile.services)[i]].environment);
                 } catch { console.log('no env') }
                 } catch { console.log('no env') }
             }
             }
-            
         } else {
         } else {
 
 
             let compose_file = `version: '3'`;
             let compose_file = `version: '3'`;
@@ -72,68 +72,80 @@ export const Install = async (req, res) => {
                 }
                 }
             }
             }
 
 
+
             // Volumes
             // Volumes
-            if (volume0 == 'on' || volume1 == 'on' || volume2 == 'on' || volume3 == 'on' || volume4 == 'on' || volume5 == 'on') {
-                compose_file += `\n    volumes:`
+            let volumes = [volume0, volume1, volume2, volume3, volume4, volume5]
 
 
-                for (let i = 0; i < 6; i++) {
+            for (let i = 0; i < volumes.length; i++) {
+                if (volumes[i] == 'on') {
+                    compose_file += `\n    volumes:`
+                    break;
+                }
+            }
 
 
-                    // if volume is on and neither bind or container is empty, it's a bind mount (ex /mnt/user/appdata/config:/config  )
-                    if ((data[`volume${i}`] == 'on') && (data[`volume_${i}_bind`] != '') && (data[`volume_${i}_container`] != '')) {
-                        compose_file += `\n      - ${data[`volume_${i}_bind`]}:${data[`volume_${i}_container`]}:${data[`volume_${i}_readwrite`]}`
-                    }
+            for (let i = 0; i < volumes.length; i++) {
 
 
-                    // if bind is empty create a docker volume (ex container_name_config:/config) convert any '/' in container name to '_'
-                    else if ((data[`volume${i}`] == 'on') && (data[`volume_${i}_bind`] == '') && (data[`volume_${i}_container`] != '')) {
-                        let volume_name = data[`volume_${i}_container`].replace(/\//g, '_');
-                        compose_file += `\n      - ${name}_${volume_name}:${data[`volume_${i}_container`]}:${data[`volume_${i}_readwrite`]}`
-                        docker_volumes.push(`${name}_${volume_name}`);
-                    } 
+                // if volume is on and neither bind or container is empty, it's a bind mount (ex /mnt/user/appdata/config:/config  )
+                if ((data[`volume${i}`] == 'on') && (data[`volume_${i}_bind`] != '') && (data[`volume_${i}_container`] != '')) {
+                    compose_file += `\n      - ${data[`volume_${i}_bind`]}:${data[`volume_${i}_container`]}:${data[`volume_${i}_readwrite`]}`
                 }
                 }
+
+                // if bind is empty create a docker volume (ex container_name_config:/config) convert any '/' in container name to '_'
+                else if ((data[`volume${i}`] == 'on') && (data[`volume_${i}_bind`] == '') && (data[`volume_${i}_container`] != '')) {
+                    let volume_name = data[`volume_${i}_container`].replace(/\//g, '_');
+                    compose_file += `\n      - ${name}_${volume_name}:${data[`volume_${i}_container`]}:${data[`volume_${i}_readwrite`]}`
+                    docker_volumes.push(`${name}_${volume_name}`);
+                } 
+                
             }
             }
 
 
             // Environment variables
             // 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:`
+            let env_vars = [env0, env1, env2, env3, env4, env5, env6, env7, env8, env9, env10, env11]
+
+            for (let i = 0; i < env_vars.length; i++) {
+                if (env_vars[i] == 'on') {
+                    compose_file += `\n    environment:`
+                    break;
+                }
             }
             }
-            for (let i = 0; i < 12; i++) {
-                if (data[`env${i}`] == 'on') {
+            for (let i = 0; i < env_vars.length; i++) {
+                if (env_vars[i] == 'on') {
                     compose_file += `\n      - ${data[`env_${i}_name`]}=${data[`env_${i}_default`]}`
                     compose_file += `\n      - ${data[`env_${i}_name`]}=${data[`env_${i}_default`]}`
+                }
+            }
 
 
+            // Labels
+            let labels = [label0, label1, label2, label3, label4, label5, label6, label7, label8, label9, label10, label11]
+
+            for (let i = 0; i < labels.length; i++) {
+                if (labels[i] == 'on') {
+                    compose_file += `\n    labels:`
+                    break;
                 }
                 }
             }
             }
 
 
-            // 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++) {
             for (let i = 0; i < 12; i++) {
                 if (data[`label${i}`] == 'on') {
                 if (data[`label${i}`] == 'on') {
                     compose_file += `\n      - ${data[`label_${i}_name`]}=${data[`label_${i}_value`]}`
                     compose_file += `\n      - ${data[`label_${i}_name`]}=${data[`label_${i}_value`]}`
                 }
                 }
             }
             }
 
 
-            // Add privileged mode 
-
+            // Privileged mode 
             if (data.privileged == 'on') {
             if (data.privileged == 'on') {
                 compose_file += `\n    privileged: true`
                 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 (data[`env${i}`] == 'on') {
-                        if (data[`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]`
-                        }
-                    }
+            // Hardware acceleration
+            for (let i = 0; i < env_vars.length; i++) {
+                if ((env_vars[i] == 'on') && (data[`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]`
+                    break;
                 }
                 }
             }
             }
 
 
@@ -165,6 +177,15 @@ export const Install = async (req, res) => {
                 (async () => {
                 (async () => {
                 await compose.pull();
                 await compose.pull();
                 await compose.up();
                 await compose.up();
+
+                const syslog = await Syslog.create({
+                    user: req.session.user,
+                    email: null,
+                    event: "App Installation",
+                    message: `${name} installed successfully}`,
+                    ip: req.socket.remoteAddress
+                });
+
                 })();
                 })();
 
 
             } catch { console.log('error running compose file')}
             } catch { console.log('error running compose file')}

+ 13 - 2
functions/uninstall.js

@@ -1,4 +1,5 @@
-import { docker } from "../app.js";
+import { docker } from "../server.js";
+import { Syslog } from "../database/models.js";
 
 
 
 
 export const Uninstall = async (req, res) => {
 export const Uninstall = async (req, res) => {
@@ -16,7 +17,17 @@ export const Uninstall = async (req, res) => {
         }
         }
         try {
         try {
             await containerName.remove();
             await containerName.remove();
-            console.log(`Removed ${service_name} container`);
+            
+
+            const syslog = await Syslog.create({
+                user: req.session.user,
+                email: null,
+                event: "App Removal",
+                message: `${name} uninstalled successfully}`,
+                ip: req.socket.remoteAddress
+            });
+
+
         } catch {
         } catch {
             console.log(`Error removing ${service_name} container`);
             console.log(`Error removing ${service_name} container`);
         }
         }

+ 1 - 1
package.json

@@ -2,7 +2,7 @@
   "name": "dweebui",
   "name": "dweebui",
   "version": "1.0.0",
   "version": "1.0.0",
   "description": "A web UI for Docker",
   "description": "A web UI for Docker",
-  "main": "app.js",
+  "main": "server.js",
   "type": "module",
   "type": "module",
   "scripts": {
   "scripts": {
     "test": "mocha --require @babel/register"
     "test": "mocha --require @babel/register"

+ 357 - 333
app.js → server.js

@@ -1,333 +1,357 @@
-import express from 'express';
-import session from 'express-session';
-import compression from 'compression';
-import helmet from 'helmet';
-import Docker from 'dockerode';
-import cors from 'cors';
-import { Readable } from 'stream';
-import { rateLimit } from 'express-rate-limit';
-import { instrument } from '@socket.io/admin-ui'
-import { router } from './router/index.js';
-import { createServer } from 'node:http';
-import { Server } from 'socket.io';
-import { sequelize, Container } from './database/models.js';
-import { currentLoad, mem, networkStats, fsSize, dockerContainerStats, dockerImages, networkInterfaces } from 'systeminformation';
-import { containerCard } from './components/containerCard.js';
-
-export const app = express();
-const server = createServer(app);
-const port = process.env.PORT || 8000;
-export var docker = new Docker();
-let [cpu, ram, tx, rx, disk] = [0, 0, 0, 0, 0];
-let [hidden, clicked, dockerEvents] = ['', false, ''];
-let metricsInterval, cardsInterval, graphsInterval;
-let cardList = '';
-const statsArray = {};
-
-// Socket.io admin ui
-export const io = new Server(server, { 
-    connectionStateRecovery: {},
-    cors: {
-        origin: ['http://localhost:8000', 'https://admin.socket.io'],
-        methods: ['GET', 'POST'],
-        credentials: true
-    } 
-});
-instrument(io, {
-    auth: false,
-    readonly: true
-});
-
-// Session middleware
-const sessionMiddleware = session({
-    secret: "keyboard cat", 
-    resave: false, 
-    saveUninitialized: false, 
-    cookie:{
-        secure:false, // Only set to true if you are using HTTPS.
-        httpOnly:false, // Only set to true if you are using HTTPS.
-        maxAge:3600000 * 8 // Session max age in milliseconds. 3600000 = 1 hour.
-    }
-});
-
-// Make session data available to socket.io
-io.engine.use(sessionMiddleware); 
-
-// Rate limiter
-const limiter = rateLimit({
-	windowMs: 5 * 60 * 1000, // 5 minutes
-	limit: 50, // Limit each IP to 50 requests per `window`.
-	standardHeaders: 'draft-7',
-	legacyHeaders: false,
-})
-
-// Express middleware
-app.set('view engine', 'ejs');
-app.use([
-    compression(),
-    cors(),
-    helmet({contentSecurityPolicy: false}),
-    express.static("public"),
-    express.json(),
-    express.urlencoded({ extended: true }),
-    sessionMiddleware,
-    router,
-    limiter
-]);
-
-// Initialize server
-server.listen(port, async () => {
-    async function init() {
-        try { await sequelize.authenticate().then(() => { console.log('[Connected to DB]') }); } 
-            catch { console.log('[Could not connect to DB]'); }
-        try { await sequelize.sync().then(() => { console.log('[Models Synced]') }); } 
-            catch { console.log('[Could not Sync Models]', error); }
-        await getHidden();
-        containerCards();
-    }
-    await init();
-    app.emit("appStarted");
-    console.log(`\nServer listening on http://localhost:${port}`);
-});
-
-// Server metrics
-let serverMetrics = async () => {
-    currentLoad().then(data => { 
-        cpu = Math.round(data.currentLoad); 
-    });
-    mem().then(data => { 
-        ram = Math.round((data.active / data.total) * 100); 
-    });
-    networkStats().then(data => { 
-        tx = data[0].tx_bytes / (1024 * 1024); 
-        rx = data[0].rx_bytes / (1024 * 1024); 
-    });
-    fsSize().then(data => { 
-        disk = data[0].use; 
-    });
-}
-
-// List docker containers
-let containerCards = async () => {
-    let list = '';
-    const allContainers = await docker.listContainers({ all: true });
-    for (const container of allContainers) {
-        if (!hidden.includes(container.Names[0].slice(1))) {
-
-            let imageVersion = container.Image.split('/');
-            let service = imageVersion[imageVersion.length - 1].split(':')[0];
-            let containerId = docker.getContainer(container.Id);
-            let containerInfo = await containerId.inspect();
-            let ports_list = [];
-            try {
-            for (const [key, value] of Object.entries(containerInfo.HostConfig.PortBindings)) {
-                let ports = {
-                    check: 'checked',
-                    external: value[0].HostPort,
-                    internal: key.split('/')[0],
-                    protocol: key.split('/')[1]
-                }
-                ports_list.push(ports);
-            }
-            } catch {}
-
-            let external_port = ports_list[0]?.external || 0;
-            let internal_port = ports_list[0]?.internal || 0;
-
-            let container_info = {
-                name: container.Names[0].slice(1),
-                service: service,
-                id: container.Id,
-                state: container.State,
-                image: container.Image,
-                external_port: external_port,
-                internal_port: internal_port,
-                ports: ports_list,
-                link: 'localhost',
-            }
-            let card = containerCard(container_info);
-            list += card;
-        }
-    }
-    cardList = list;
-}
-
-// Container metrics
-let containerStats = async () => {
-    const data = await docker.listContainers({ all: true });
-    for (const container of data) {
-        if (!hidden.includes(container.Names[0].slice(1))) {
-            const stats = await dockerContainerStats(container.Id);
-            const name = container.Names[0].slice(1);
-
-            if (!statsArray[name]) {
-                statsArray[name] = {
-                    cpuArray: Array(15).fill(0),
-                    ramArray: Array(15).fill(0)
-                };
-            }
-            statsArray[name].cpuArray.push(Math.round(stats[0].cpuPercent));
-            statsArray[name].ramArray.push(Math.round(stats[0].memPercent));
-
-            statsArray[name].cpuArray = statsArray[name].cpuArray.slice(-15);
-            statsArray[name].ramArray = statsArray[name].ramArray.slice(-15);
-        }
-    }
-}
-
-// Store docker events 
-docker.getEvents((err, stream) => {
-    if (err) throw err;
-    stream.on('data', (chunk) => {
-        dockerEvents += chunk.toString('utf8');
-    });
-});
-
-// Check for docker events
-setInterval( () => {
-    if (dockerEvents != '') {
-        getHidden();
-        containerCards();
-        dockerEvents = '';
-    }
-}, 1000);
-
-// Get hidden containers
-async function getHidden() {
-    hidden = await Container.findAll({ where: {visibility:false}});
-}
-
-// Socket.io
-io.on('connection', (socket) => {
-    let sessionData = socket.request.session;
-    let sent = '';
-    if (sessionData.user != undefined) {
-        console.log(`${sessionData.user} connected from ${socket.handshake.headers.host}`);
-        
-        // Start intervals if not already started
-        if (!metricsInterval) {
-            serverMetrics();
-            metricsInterval = setInterval(serverMetrics, 1000);
-            console.log('Metrics interval started');
-        }
-        if (!cardsInterval) {
-            containerCards();
-            cardsInterval = setInterval(containerCards, 1000);
-            console.log('Cards interval started');
-        }
-        if (!graphsInterval) {
-            containerStats();
-            graphsInterval = setInterval(containerStats, 1000);
-            console.log('Graphs interval started');
-        }
-
-        setInterval(() => {
-            socket.emit('metrics', [cpu, ram, tx, rx, disk]);
-            if (sent != cardList) {
-                sent = cardList;
-                socket.emit('containers', cardList);
-            }
-            socket.emit('containerStats', statsArray);
-        }, 1000);
-
-
-
-        
-        socket.on('clicked', (data) => {
-            if (clicked == true) { return; } clicked = true;
-            let { name, id, value } = data;
-            console.log(`${sessionData.user} clicked: ${id} ${value} ${name}`);
-
-
-            if (id == 'logs'){
-                function containerLogs (data) {
-                    return new Promise((resolve, reject) => {
-                        let logString = '';
-                
-                        var options = {
-                            follow: false,
-                            stdout: true,
-                            stderr: false,
-                            timestamps: false
-                        };
-                
-                        var containerName = docker.getContainer(data);
-                
-                        containerName.logs(options, function (err, stream) {
-                            if (err) {
-                                reject(err);
-                                return;
-                            }
-                
-                            const readableStream = Readable.from(stream);
-                
-                            readableStream.on('data', function (chunk) {
-                                logString += chunk.toString('utf8');
-                            });
-                
-                            readableStream.on('end', function () {
-                                resolve(logString);
-                            });
-                        });
-                    });
-                };
-                containerLogs(name).then((data) => {
-                    socket.emit('logs', data);
-                }).catch((err) => {
-                    console.log(err);
-                });
-
-            }
-
-
-            if (id == 'start' || id == 'stop' || id == 'pause' || id == 'restart'){
-                var containerName = docker.getContainer(name);
-        
-                if ((id == 'start') && (value == 'stopped')) {
-                    containerName.start();
-                } else if ((id == 'start') && (value == 'paused')) {
-                    containerName.unpause();
-                } else if ((id == 'stop') && (value != 'stopped')) {
-                    containerName.stop();
-                } else if ((id == 'pause') && (value == 'running')) {
-                    containerName.pause();
-                } else if ((id == 'pause') && (value == 'paused')) {
-                    containerName.unpause();
-                } else if (id == 'restart') {
-                    containerName.restart();
-                }
-            }
-            clicked = false;
-        });
-
-        socket.on('disconnect', () => {
-            console.log(`${sessionData.user} disconnected`);
-            socket.disconnect();
-            // clear intervals if no users are connected
-            if (io.engine.clientsCount == 0) {
-                clearInterval(metricsInterval);
-                clearInterval(cardsInterval);
-                clearInterval(graphsInterval);
-                metricsInterval = null;
-                cardsInterval = null;
-                graphsInterval = null;
-                console.log('All intervals cleared');
-            }
-        });
-    } else {
-        console.log('Missing session data');
-    }
-});
-
-
-
-
-
-
-
-
-// let link = '';
-// networkInterfaces().then(data => {
-//     link = data[0].ip4;
-// });
-
+import express from 'express';
+import session from 'express-session';
+import compression from 'compression';
+import helmet from 'helmet';
+import Docker from 'dockerode';
+import cors from 'cors';
+import { Readable } from 'stream';
+import { rateLimit } from 'express-rate-limit';
+import { instrument } from '@socket.io/admin-ui'
+import { router } from './router/index.js';
+import { createServer } from 'node:http';
+import { Server } from 'socket.io';
+import { sequelize, Container } from './database/models.js';
+import { currentLoad, mem, networkStats, fsSize, dockerContainerStats, dockerImages, networkInterfaces } from 'systeminformation';
+import { containerCard } from './components/containerCard.js';
+
+export const app = express();
+const server = createServer(app);
+const port = process.env.PORT || 8000;
+export var docker = new Docker();
+let [cpu, ram, tx, rx, disk] = [0, 0, 0, 0, 0];
+let [hidden, clicked, dockerEvents] = ['', false, ''];
+let metricsInterval, cardsInterval, graphsInterval;
+let cardList = '';
+const statsArray = {};
+
+// Socket.io admin ui
+export const io = new Server(server, { 
+    connectionStateRecovery: {},
+    cors: {
+        origin: ['http://localhost:8000', 'https://admin.socket.io'],
+        methods: ['GET', 'POST'],
+        credentials: true
+    } 
+});
+instrument(io, {
+    auth: false,
+    readonly: true
+});
+
+// Session middleware
+const sessionMiddleware = session({
+    secret: "keyboard cat", 
+    resave: false, 
+    saveUninitialized: false, 
+    cookie:{
+        secure:false, // Only set to true if you are using HTTPS.
+        httpOnly:false, // Only set to true if you are using HTTPS.
+        maxAge:3600000 * 8 // Session max age in milliseconds. 3600000 = 1 hour.
+    }
+});
+
+// Make session data available to socket.io
+io.engine.use(sessionMiddleware); 
+
+// Rate limiter
+const limiter = rateLimit({
+	windowMs: 5 * 60 * 1000, // 5 minutes
+	limit: 50, // Limit each IP to 50 requests per `window`.
+	standardHeaders: 'draft-7',
+	legacyHeaders: false,
+})
+
+// Express middleware
+app.set('view engine', 'ejs');
+app.use([
+    compression(),
+    cors(),
+    helmet({contentSecurityPolicy: false}),
+    express.static("public"),
+    express.json(),
+    express.urlencoded({ extended: true }),
+    sessionMiddleware,
+    router,
+    limiter
+]);
+
+// Initialize server
+server.listen(port, async () => {
+    async function init() {
+        try { await sequelize.authenticate().then(() => { console.log('[Connected to DB]') }); } 
+            catch { console.log('[Could not connect to DB]'); }
+        try { await sequelize.sync().then(() => { console.log('[Models Synced]') }); } 
+            catch { console.log('[Could not Sync Models]', error); }
+        await getHidden();
+        containerCards();
+    }
+    await init();
+    app.emit("appStarted");
+    console.log(`\nServer listening on http://localhost:${port}`);
+});
+
+// Server metrics
+let serverMetrics = async () => {
+    currentLoad().then(data => { 
+        cpu = Math.round(data.currentLoad); 
+    });
+    mem().then(data => { 
+        ram = Math.round((data.active / data.total) * 100); 
+    });
+    networkStats().then(data => { 
+        tx = data[0].tx_bytes / (1024 * 1024); 
+        rx = data[0].rx_bytes / (1024 * 1024); 
+    });
+    fsSize().then(data => { 
+        disk = data[0].use; 
+    });
+}
+
+// List docker containers
+let containerCards = async () => {
+    let list = '';
+    const allContainers = await docker.listContainers({ all: true });
+    for (const container of allContainers) {
+        if (!hidden.includes(container.Names[0].slice(1))) {
+
+            let imageVersion = container.Image.split('/');
+            let service = imageVersion[imageVersion.length - 1].split(':')[0];
+            let containerId = docker.getContainer(container.Id);
+            let containerInfo = await containerId.inspect();
+            let ports_list = [];
+            try {
+            for (const [key, value] of Object.entries(containerInfo.HostConfig.PortBindings)) {
+                let ports = {
+                    check: 'checked',
+                    external: value[0].HostPort,
+                    internal: key.split('/')[0],
+                    protocol: key.split('/')[1]
+                }
+                ports_list.push(ports);
+            }
+            } catch {}
+
+            let external_port = ports_list[0]?.external || 0;
+            let internal_port = ports_list[0]?.internal || 0;
+
+            let container_info = {
+                name: container.Names[0].slice(1),
+                service: service,
+                id: container.Id,
+                state: container.State,
+                image: container.Image,
+                external_port: external_port,
+                internal_port: internal_port,
+                ports: ports_list,
+                link: 'localhost',
+            }
+            let card = containerCard(container_info);
+            list += card;
+        }
+    }
+    cardList = list;
+}
+
+// Container metrics
+let containerStats = async () => {
+    const data = await docker.listContainers({ all: true });
+    for (const container of data) {
+        if (!hidden.includes(container.Names[0].slice(1))) {
+            const stats = await dockerContainerStats(container.Id);
+            const name = container.Names[0].slice(1);
+
+            if (!statsArray[name]) {
+                statsArray[name] = {
+                    cpuArray: Array(15).fill(0),
+                    ramArray: Array(15).fill(0)
+                };
+            }
+            statsArray[name].cpuArray.push(Math.round(stats[0].cpuPercent));
+            statsArray[name].ramArray.push(Math.round(stats[0].memPercent));
+
+            statsArray[name].cpuArray = statsArray[name].cpuArray.slice(-15);
+            statsArray[name].ramArray = statsArray[name].ramArray.slice(-15);
+        }
+    }
+}
+
+// Store docker events 
+docker.getEvents((err, stream) => {
+    if (err) throw err;
+    stream.on('data', (chunk) => {
+        dockerEvents += chunk.toString('utf8');
+    });
+});
+
+// Check for docker events
+setInterval( () => {
+    if (dockerEvents != '') {
+        getHidden();
+        containerCards();
+        dockerEvents = '';
+    }
+}, 1000);
+
+// Get hidden containers
+async function getHidden() {
+    hidden = await Container.findAll({ where: {visibility:false}});
+    hidden = hidden.map((container) => container.name);
+}
+
+// Socket.io
+io.on('connection', (socket) => {
+    let sessionData = socket.request.session;
+    let sent = '';
+    if (sessionData.user != undefined) {
+        console.log(`${sessionData.user} connected from ${socket.handshake.headers.host}`);
+        
+        // Start intervals if not already started
+        if (!metricsInterval) {
+            serverMetrics();
+            metricsInterval = setInterval(serverMetrics, 1000);
+            console.log('Metrics interval started');
+        }
+        if (!cardsInterval) {
+            containerCards();
+            cardsInterval = setInterval(containerCards, 1000);
+            console.log('Cards interval started');
+        }
+        if (!graphsInterval) {
+            containerStats();
+            graphsInterval = setInterval(containerStats, 1000);
+            console.log('Graphs interval started');
+        }
+
+        setInterval(() => {
+            socket.emit('metrics', [cpu, ram, tx, rx, disk]);
+            if (sent != cardList) {
+                sent = cardList;
+                socket.emit('containers', cardList);
+            }
+            socket.emit('containerStats', statsArray);
+        }, 1000);
+
+
+
+        // Client input        
+        socket.on('clicked', (data) => {
+            if (clicked == true) { return; } clicked = true;
+            let { name, id, value } = data;
+            console.log(`${sessionData.user} clicked: ${id} ${value} ${name}`);
+
+            // View container logs
+            if (id == 'logs'){
+                function containerLogs (data) {
+                    return new Promise((resolve, reject) => {
+                        let logString = '';
+                
+                        var options = {
+                            follow: false,
+                            stdout: true,
+                            stderr: false,
+                            timestamps: false
+                        };
+                
+                        var containerName = docker.getContainer(data);
+                
+                        containerName.logs(options, function (err, stream) {
+                            if (err) {
+                                reject(err);
+                                return;
+                            }
+                
+                            const readableStream = Readable.from(stream);
+                
+                            readableStream.on('data', function (chunk) {
+                                logString += chunk.toString('utf8');
+                            });
+                
+                            readableStream.on('end', function () {
+                                resolve(logString);
+                            });
+                        });
+                    });
+                };
+                containerLogs(name).then((data) => {
+                    socket.emit('logs', data);
+                }).catch((err) => {
+                    console.log(err);
+                });
+
+            }
+
+            // start, stop, pause, restart container
+            if (id == 'start' || id == 'stop' || id == 'pause' || id == 'restart'){
+                var containerName = docker.getContainer(name);
+        
+                if ((id == 'start') && (value == 'stopped')) {
+                    containerName.start();
+                } else if ((id == 'start') && (value == 'paused')) {
+                    containerName.unpause();
+                } else if ((id == 'stop') && (value != 'stopped')) {
+                    containerName.stop();
+                } else if ((id == 'pause') && (value == 'running')) {
+                    containerName.pause();
+                } else if ((id == 'pause') && (value == 'paused')) {
+                    containerName.unpause();
+                } else if (id == 'restart') {
+                    containerName.restart();
+                }
+            }
+
+            // hide container
+            if (id == 'hide') {
+                async function hideContainer() {
+                    let containerExists = await Container.findOne({ where: {name: name}});
+                    if(!containerExists) {
+                        const newContainer = await Container.create({ name: name, visibility: false, });
+                        getHidden();
+                    } else {
+                        containerExists.update({ visibility: false });
+                        getHidden();
+                    }
+                    
+                }
+                hideContainer();
+            }
+
+            // unhide containers
+            if (id == 'resetView') {
+                Container.update({ visibility: true }, { where: {} });
+                getHidden();
+            }
+                
+            clicked = false;
+        });
+
+        socket.on('disconnect', () => {
+            console.log(`${sessionData.user} disconnected`);
+            socket.disconnect();
+            // clear intervals if no users are connected
+            if (io.engine.clientsCount == 0) {
+                clearInterval(metricsInterval);
+                clearInterval(cardsInterval);
+                clearInterval(graphsInterval);
+                metricsInterval = null;
+                cardsInterval = null;
+                graphsInterval = null;
+                console.log('All intervals cleared');
+            }
+        });
+    } else {
+        console.log('Missing session data');
+    }
+});
+
+
+
+
+
+
+
+
+// let link = '';
+// networkInterfaces().then(data => {
+//     link = data[0].ip4;
+// });
+