瀏覽代碼

Add files via upload

lllllllillllllillll 1 年之前
父節點
當前提交
9597df2524
共有 6 個文件被更改,包括 405 次插入51 次删除
  1. 4 26
      app.js
  2. 67 1
      controllers/apps.js
  3. 161 0
      functions/package_manager.js
  4. 150 0
      functions/system_information.js
  5. 7 8
      routes/index.js
  6. 16 16
      templates.json

+ 4 - 26
app.js

@@ -4,7 +4,7 @@ const redis = require('connect-redis');
 const app = express();
 const app = express();
 const routes = require("./routes");
 const routes = require("./routes");
 
 
-const { serverStats, containerList, containerStats, containerAction } = require('./functions/systeminformation');
+const { serverStats, containerList, containerStats, containerAction } = require('./functions/system_information');
 
 
 let sent_list, clicked;
 let sent_list, clicked;
 
 
@@ -43,8 +43,6 @@ const io = require('socket.io')(server);
 io.engine.use(sessionMiddleware);
 io.engine.use(sessionMiddleware);
 
 
 
 
-
-
 io.on('connection', (socket) => {
 io.on('connection', (socket) => {
     // set user session
     // set user session
     const user_session = socket.request.session;
     const user_session = socket.request.session;
@@ -54,22 +52,16 @@ io.on('connection', (socket) => {
     if (sent_list != null) { socket.emit('cards', sent_list); }
     if (sent_list != null) { socket.emit('cards', sent_list); }
 
 
     // check if an install card has to be sent
     // check if an install card has to be sent
-    if((app.locals.install != '') && (app.locals.install != null)){
-        socket.emit('install', app.locals.install);
-    }    
+    if((app.locals.install != '') && (app.locals.install != null)){ socket.emit('install', app.locals.install); }
 
 
     // send server metrics
     // send server metrics
     let ServerStats = setInterval(async () => {
     let ServerStats = setInterval(async () => {
-
         socket.emit('metrics', await serverStats());
         socket.emit('metrics', await serverStats());
-
     }, 1000);
     }, 1000);
 
 
-    // send container metrics
+    // send container list
     let ContainerList = setInterval(async () => {
     let ContainerList = setInterval(async () => {
-
         let card_list = await containerList();
         let card_list = await containerList();
-
         if (sent_list !== card_list) {
         if (sent_list !== card_list) {
             sent_list = card_list;
             sent_list = card_list;
             app.locals.install = '';
             app.locals.install = '';
@@ -80,20 +72,14 @@ io.on('connection', (socket) => {
     // send container metrics
     // send container metrics
     let ContainerStats = setInterval(async () => {
     let ContainerStats = setInterval(async () => {
         let container_stats = await containerStats();
         let container_stats = await containerStats();
-
-
-        //split up the array to display the name and stats
         for (let i = 0; i < container_stats.length; i++) {
         for (let i = 0; i < container_stats.length; i++) {
             socket.emit('container_stats', container_stats[i]);
             socket.emit('container_stats', container_stats[i]);
         }
         }
-
     }, 1000);
     }, 1000);
 
 
-
+    // play/pause/stop/restart container
     socket.on('clicked', (data) => {
     socket.on('clicked', (data) => {
-        // Prevent multiple clicks
         if (clicked == true) { return; } clicked = true;
         if (clicked == true) { return; } clicked = true;
-
         let buttonPress = {
         let buttonPress = {
             user: socket.request.session.user,
             user: socket.request.session.user,
             role: socket.request.session.role,
             role: socket.request.session.role,
@@ -101,22 +87,14 @@ io.on('connection', (socket) => {
             container: data.container,
             container: data.container,
             state: data.state
             state: data.state
         }
         }
-
-        console.log(buttonPress)
-
         containerAction(buttonPress);
         containerAction(buttonPress);
-
         clicked = false;
         clicked = false;
     });
     });
     
     
-
     socket.on('disconnect', () => {                
     socket.on('disconnect', () => {                
         clearInterval(ServerStats);
         clearInterval(ServerStats);
         clearInterval(ContainerList);
         clearInterval(ContainerList);
         clearInterval(ContainerStats);
         clearInterval(ContainerStats);
     }); 
     }); 
 
 
-
-    
-
 });
 });

+ 67 - 1
controllers/apps.js

@@ -1,5 +1,12 @@
 const User = require('../database/UserModel');
 const User = require('../database/UserModel');
 const { appCard } = require('../components/appCard')
 const { appCard } = require('../components/appCard')
+const { exec, execSync } = require("child_process");
+const { dashCard } = require('../components/dashCard');
+const yaml = require('js-yaml');
+
+const { install } = require('../functions/package_manager');
+
+
 const templates_json = require('../templates.json');
 const templates_json = require('../templates.json');
 let templates = templates_json.templates;
 let templates = templates_json.templates;
 
 
@@ -60,7 +67,7 @@ exports.Apps = async function(req, res) {
 
 
 
 
 
 
-exports.processApps = async function(req, res) {
+exports.searchApps = async function(req, res) {
     if (req.session.role == "admin") {
     if (req.session.role == "admin") {
 
 
         // Get the user.
         // Get the user.
@@ -127,4 +134,63 @@ exports.processApps = async function(req, res) {
         // Redirect to the login page
         // Redirect to the login page
         res.redirect("/login");
         res.redirect("/login");
     }
     }
+}
+
+
+
+
+
+
+
+
+
+exports.Install = async function (req, res) {
+    
+    if (req.session.role == "admin") {
+
+        install(req.body);
+
+        let container_info = {
+            name: req.body.name,
+            service: req.body.service_name,
+            state: 'installing',
+            image: req.body.image,
+            restart_policy: req.body.restart_policy
+        }
+
+        let installCard = dashCard(container_info);
+
+        req.app.locals.install = installCard;
+
+        
+        // Redirect to the home page
+        res.redirect("/");
+    } else {
+        // Redirect to the login page
+        res.redirect("/login");
+    }
+}
+
+
+
+exports.Uninstall = async function (req, res) {
+    
+    if (req.session.role == "admin") {
+
+
+        if (req.body.confirm == 'Yes') {
+            exec(`docker compose -f ./appdata/${req.body.service_name}/docker-compose.yml down`, (error, stdout, stderr) => {
+                if (error) { console.error(`error: ${error.message}`); return; }
+                if (stderr) { console.error(`stderr: ${stderr}`); return; }
+                console.log(`stdout:\n${stdout}`);
+            });
+        }
+
+
+        // Redirect to the home page
+        res.redirect("/");
+    } else {
+        // Redirect to the login page
+        res.redirect("/login");
+    }
 }
 }

+ 161 - 0
functions/package_manager.js

@@ -0,0 +1,161 @@
+const { writeFileSync, mkdirSync, readFileSync } = require("fs");
+const { exec, execSync } = require("child_process");
+const { dashCard } = require('../components/dashCard');
+const yaml = require('js-yaml');
+
+
+
+module.exports.install = async function (data) {
+    
+
+        let { service_name, name, image, command_check, command, net_mode, restart_policy } = data;        
+        let { port0, port1, port2, port3, port4, port5 } = data;
+        let { volume0, volume1, volume2, volume3, volume4, volume5 } = data;
+        let { env0, env1, env2, env3, env4, env5, env6, env7, env8, env9, env10, env11 } = data;
+        let { label0, label1, label2, label3, label4, label5, label6, label7, label8, label9, label10, label11 } = data;
+
+        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}`
+            }
+
+            // 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 (data[`port${i}`] == 'on') {
+                            compose_file += `\n      - ${data[`port_${i}_external`]}:${data[`port_${i}_internal`]}/${data[`port_${i}_protocol`]}`
+                        }
+                    }
+            }
+
+            // 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 (data[`volume${i}`] == 'on') {
+                        compose_file += `\n      - ${data[`volume_${i}_bind`]}:${data[`volume_${i}_container`]}:${data[`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 (data[`env${i}`] == 'on') {
+                    compose_file += `\n      - ${data[`env_${i}_name`]}=${data[`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 (data[`label${i}`] == 'on') {
+                    compose_file += `\n      - ${data[`label_${i}_name`]}=${data[`label_${i}_value`]}`
+                }
+            }
+
+            // Add privileged mode 
+
+            if (data.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 (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]`
+                        }
+                    }
+                }
+            }
+
+            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') }
+
+        }
+
+
+}
+
+
+
+module.exports.uninstall = async function (data) {
+    
+    if (req.session.role == "admin") {
+
+
+        if (data.confirm == 'Yes') {
+            exec(`docker compose -f ./appdata/${data.service_name}/docker-compose.yml down`, (error, stdout, stderr) => {
+                if (error) { console.error(`error: ${error.message}`); return; }
+                if (stderr) { console.error(`stderr: ${stderr}`); return; }
+                console.log(`stdout:\n${stdout}`);
+            });
+        }
+
+
+        // Redirect to the home page
+        res.redirect("/");
+    } else {
+        // Redirect to the login page
+        res.redirect("/login");
+    }
+}

+ 150 - 0
functions/system_information.js

@@ -0,0 +1,150 @@
+const { currentLoad, mem, networkStats, fsSize, dockerContainerStats } = require('systeminformation');
+var Docker = require('dockerode');
+var docker = new Docker({ socketPath: '/var/run/docker.sock' });
+const { dashCard } = require('../components/dashCard');
+
+
+
+module.exports.serverStats = async function () {
+    const cpuUsage = await currentLoad();
+    const ramUsage = await mem();
+    const netUsage = await networkStats();
+    const diskUsage = await fsSize();
+
+    const info = {
+        cpu: Math.round(cpuUsage.currentLoad),
+        ram: Math.round((ramUsage.active / ramUsage.total) * 100),
+        tx: netUsage[0].tx_bytes,
+        rx: netUsage[0].rx_bytes,
+        disk: diskUsage[0].use,
+    };
+
+    return info;
+}
+
+
+
+module.exports.containerList = async function () {
+    let card_list = '';
+
+    const data = await docker.listContainers({ all: true });
+    for (const container of data) {
+        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 open_ports = [];
+        let external_port = 0;
+        let internal_port = 0;
+
+        for (const [key, value] of Object.entries(containerInfo.HostConfig.PortBindings)) {
+            open_ports.push(`${value[0].HostPort}`);
+            external_port = value[0].HostPort;
+            internal_port = key;
+
+            if ((external_port == undefined) || (internal_port == undefined)) {
+                external_port = 0;
+                internal_port = 0;
+            }
+        }
+
+        let volumes = [];
+        for (const [key, value] of Object.entries(containerInfo.Mounts)) {
+            volumes.push(`${value.Source}: ${value.Destination}: ${value.RW}`);
+        }
+
+        let environment_variables = [];
+        for (const [key, value] of Object.entries(containerInfo.Config.Env)) {
+            environment_variables.push(`${key}: ${value}`);
+        }
+
+        let labels = [];
+        for (const [key, value] of Object.entries(containerInfo.Config.Labels)) {
+            labels.push(`${key}: ${value}`);
+        }
+
+
+        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
+        }
+
+        let dockerCard = dashCard(container_info);
+
+        card_list += dockerCard;
+        
+    }
+
+    return card_list;
+}
+
+
+
+
+
+
+
+module.exports.containerStats = async function () {
+
+    let container_stats = [];
+
+    const data = await docker.listContainers({ all: true });
+
+    for (const container of data) {
+
+        const stats = await dockerContainerStats(container.Id);
+        let container_stat = {
+            name: container.Names[0].slice(1),
+            cpu: Math.round(stats[0].cpuPercent),
+            ram: Math.round(stats[0].memPercent)
+        }
+
+        //push stats to an array
+        container_stats.push(container_stat);
+    }
+    return container_stats;
+}
+
+
+
+
+
+
+module.exports.containerAction = async function (data) {
+
+    let { user, role, action, container, state } = data;
+
+    console.log(`${user} wants to: ${action} ${container}`);
+    
+    if (role == 'admin') {
+        var containerName = docker.getContainer(container);
+
+        if ((action == 'start') && (state == 'stopped')) {
+            containerName.start();
+        } else if ((action == 'start') && (state == 'paused')) {
+            containerName.unpause();
+        } else if ((action == 'stop') && (state != 'stopped')) {
+            containerName.stop();
+        } else if ((action == 'pause') && (state == 'running')) {
+            containerName.pause();
+        } else if ((action == 'pause') && (state == 'paused')) {
+            containerName.unpause();
+        } else if (action == 'restart') {
+            containerName.restart();
+        }
+    } else {
+        console.log('User is not an admin');
+    }
+}
+
+
+
+
+

+ 7 - 8
routes/index.js

@@ -4,15 +4,14 @@ const router = express.Router();
 const { Dashboard } = require("../controllers/dashboard");
 const { Dashboard } = require("../controllers/dashboard");
 
 
 const { AddSite, RemoveSite, RefreshSites, DisableSite, EnableSite } = require("../controllers/site_actions");
 const { AddSite, RemoveSite, RefreshSites, DisableSite, EnableSite } = require("../controllers/site_actions");
-const { Install, Uninstall } = require("../controllers/app_actions");
 
 
-const {Apps, processApps} = require("../controllers/apps");
+const { Apps, searchApps, Install, Uninstall } = require("../controllers/apps");
 const { Users } = require("../controllers/users");
 const { Users } = require("../controllers/users");
-const {Account} = require("../controllers/account");
-const {Settings} = require("../controllers/settings");
-const {Logout} = require("../controllers/logout");
-const {Login, processLogin} = require("../controllers/login");
-const {Register, processRegister} = require("../controllers/register");
+const { Account } = require("../controllers/account");
+const { Settings } = require("../controllers/settings");
+const { Logout } = require("../controllers/logout");
+const { Login, processLogin } = require("../controllers/login");
+const { Register, processRegister } = require("../controllers/register");
 
 
 
 
 
 
@@ -31,7 +30,7 @@ router.post("/enablesite", EnableSite)
 router.get("/users", Users);
 router.get("/users", Users);
 
 
 router.get("/apps", Apps);
 router.get("/apps", Apps);
-router.post("/apps", processApps);
+router.post("/apps", searchApps);
 
 
 router.get("/settings", Settings);
 router.get("/settings", Settings);
 router.get("/account", Account);
 router.get("/account", Account);

+ 16 - 16
templates.json

@@ -2196,20 +2196,20 @@
       ],
       ],
       "volumes": [
       "volumes": [
         {
         {
-          "container": "/opt",
-          "bind": "/home/docker/kasm/opt"
+          "bind": "/home/docker/kasm/opt",
+          "container": "/opt"
         },
         },
         {
         {
-          "container": "/profiles",
-          "bind": "/home/docker/kasm/profiles"
+          "bind": "/home/docker/kasm/profiles",
+          "container": "/profiles"
         },
         },
         {
         {
-          "container": "/dev/input",
-          "bind": "/dev/input"
+          "bind": "/dev/input",
+          "container": "/dev/input"
         },
         },
         {
         {
-          "container": "/run/udev/data",
-          "bind": "/run/udev/data"
+          "bind": "/run/udev/data",
+          "container": "/run/udev/data"
         }
         }
       ],
       ],
       "restart_policy": "unless-stopped"
       "restart_policy": "unless-stopped"
@@ -2225,7 +2225,7 @@
         "Tools"
         "Tools"
       ],
       ],
       "platform": "linux",
       "platform": "linux",
-      "logo": "https://raw.githubusercontent.com/linuxserver/docker-templates/master/linuxserver.io/img/libreoffice-logo.png",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/libreoffice.png",
       "image": "linuxserver/libreoffice:latest",
       "image": "linuxserver/libreoffice:latest",
       "env": [
       "env": [
         {
         {
@@ -2511,7 +2511,7 @@
         "Tools"
         "Tools"
       ],
       ],
       "platform": "linux",
       "platform": "linux",
-      "logo": "https://raw.githubusercontent.com/linuxserver/docker-templates/master/linuxserver.io/img/phpmyadmin-logo.png",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/phpmyadmin.png",
       "image": "linuxserver/phpmyadmin:latest",
       "image": "linuxserver/phpmyadmin:latest",
       "env": [
       "env": [
         {
         {
@@ -2561,9 +2561,9 @@
       "title": "Pidgin",
       "title": "Pidgin",
       "name": "pidgin",
       "name": "pidgin",
       "note": "",
       "note": "",
-      "description": "Pidgin (https://pidgin.im/) is a chat program which lets you log into accounts on multiple chat networks simultaneously. This means that you can be chatting with friends on XMPP and sitting in an IRC channel at the same time.",
+      "description": "Pidgin is a chat program which lets you log into accounts on multiple chat networks simultaneously. This means that you can be chatting with friends on XMPP and sitting in an IRC channel at the same time.",
       "platform": "linux",
       "platform": "linux",
-      "logo": "https://raw.githubusercontent.com/linuxserver/docker-templates/master/linuxserver.io/img/pidgin-logo.png",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/pidgin.png",
       "image": "linuxserver/pidgin:latest",
       "image": "linuxserver/pidgin:latest",
       "env": [
       "env": [
         {
         {
@@ -2591,8 +2591,8 @@
       ],
       ],
       "volumes": [
       "volumes": [
         {
         {
-          "container": "/config",
-          "bind": "/home/docker/pidgin/config"
+          "bind": "/home/docker/pidgin",
+          "container": "/config"
         }
         }
       ],
       ],
       "restart_policy": "unless-stopped"
       "restart_policy": "unless-stopped"
@@ -2602,9 +2602,9 @@
       "title": "Remmina",
       "title": "Remmina",
       "name": "remmina",
       "name": "remmina",
       "note": "",
       "note": "",
-      "description": "[Remmina](https://remmina.org/) is a remote desktop client written in GTK, aiming to be useful for system administrators and travellers, who need to work with lots of remote computers in front of either large or tiny screens. Remmina supports multiple network protocols, in an integrated and consistent user interface. Currently RDP, VNC, SPICE, NX, XDMCP, SSH and EXEC are supported.",
+      "description": "Remmina is a remote desktop client written in GTK, aiming to be useful for system administrators and travellers, who need to work with lots of remote computers in front of either large or tiny screens. Remmina supports multiple network protocols, in an integrated and consistent user interface. Currently RDP, VNC, SPICE, NX, XDMCP, SSH and EXEC are supported.",
       "platform": "linux",
       "platform": "linux",
-      "logo": "https://raw.githubusercontent.com/linuxserver/docker-templates/master/linuxserver.io/img/remmina-icon.png",
+      "logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/remmina.png",
       "image": "linuxserver/remmina:latest",
       "image": "linuxserver/remmina:latest",
       "env": [
       "env": [
         {
         {