Selaa lähdekoodia

updated dependencies and updated router auth

lllllllillllllillll 1 vuosi sitten
vanhempi
commit
c9d7dea132
7 muutettua tiedostoa jossa 129 lisäystä ja 58 poistoa
  1. 2 2
      README.md
  2. 67 3
      controllers/portal.js
  3. 12 20
      package-lock.json
  4. 2 2
      package.json
  5. 30 26
      router/index.js
  6. 10 0
      views/partials/navbar.html
  7. 6 5
      views/portal.html

+ 2 - 2
README.md

@@ -4,7 +4,7 @@
 <p align="center">
 <p align="center">
     <a href=""><img src="https://img.shields.io/github/stars/lllllllillllllillll/DweebUI?style=flat"/></a>
     <a href=""><img src="https://img.shields.io/github/stars/lllllllillllllillll/DweebUI?style=flat"/></a>
     <a href="https://github.com/lllllllillllllillll"><img src="https://img.shields.io/github/commit-activity/y/lllllllillllllillll/DweebUI%2Fdev"/></a>
     <a href="https://github.com/lllllllillllllillll"><img src="https://img.shields.io/github/commit-activity/y/lllllllillllllillll/DweebUI%2Fdev"/></a>
-    <a href="https://github.com/lllllllillllllillll/DweebUI"><img src="https://img.shields.io/github/last-commit/lllllllillllllillll/DweebUI%2Fdev"/></a>
+    <a href="https://github.com/lllllllillllllillll/DweebUI%2Fdev"><img src="https://img.shields.io/github/last-commit/lllllllillllllillll/DweebUI%2Fdev"/></a>
     <a href="https://hub.docker.com/r/lllllllillllllillll/dweebui"><img src="https://img.shields.io/docker/pulls/lllllllillllllillll/dweebui"/></a>
     <a href="https://hub.docker.com/r/lllllllillllllillll/dweebui"><img src="https://img.shields.io/docker/pulls/lllllllillllllillll/dweebui"/></a>
     <a href="https://github.com/lllllllillllllillll/DweebUI/blob/main/LICENSE"><img src="https://img.shields.io/github/license/lllllllillllllillll/DweebUI"/></a>
     <a href="https://github.com/lllllllillllllillll/DweebUI/blob/main/LICENSE"><img src="https://img.shields.io/github/license/lllllllillllllillll/DweebUI"/></a>
     <a href="https://www.reddit.com/r/dweebui"><img src="https://img.shields.io/badge/reddit-orange"/></a>
     <a href="https://www.reddit.com/r/dweebui"><img src="https://img.shields.io/badge/reddit-orange"/></a>
@@ -27,7 +27,7 @@
 * [x] Light/Dark Mode.
 * [x] Light/Dark Mode.
 * [x] Mobile Friendly.
 * [x] Mobile Friendly.
 * [x] Easy to install app templates.
 * [x] Easy to install app templates.
-* [x] Multi-User built-in.
+* [x] Multiple Users.
 * [ ] Permissions system (in development).
 * [ ] Permissions system (in development).
 * [x] Support for Windows, Linux, and MacOS.
 * [x] Support for Windows, Linux, and MacOS.
 * [ ] Docker compose (in development).
 * [ ] Docker compose (in development).

+ 67 - 3
controllers/portal.js

@@ -3,7 +3,6 @@ import { Permission, Container, User } from '../database/models.js';
 import { docker } from '../server.js';
 import { docker } from '../server.js';
 import { dockerContainerStats } from 'systeminformation';
 import { dockerContainerStats } from 'systeminformation';
 import { readFileSync } from 'fs';
 import { readFileSync } from 'fs';
-import { currentLoad, mem, networkStats, fsSize } from 'systeminformation';
 
 
 let hidden = '';
 let hidden = '';
 
 
@@ -21,6 +20,7 @@ export const Portal = (req, res) => {
     });
     });
 }
 }
 
 
+
 async function containerInfo (containerName) {
 async function containerInfo (containerName) {
     let container = docker.getContainer(containerName);
     let container = docker.getContainer(containerName);
     let info = await container.inspect();
     let info = await container.inspect();
@@ -126,6 +126,72 @@ export async function addCard (name, state) {
 
 
 
 
 
 
+
+// HTMX server-side events
+export const SSE = (req, res) => {
+    res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive' });
+
+    let eventCheck = setInterval(async () => {
+        // builds array of containers and their states
+        containersArray = [];
+        await docker.listContainers({ all: true }).then(containers => {
+            containers.forEach(container => {
+                let name = container.Names[0].replace('/', '');
+                if (!hidden.includes(name)) { // if not hidden
+                    containersArray.push({ container: name, state: container.State });
+                } 
+            });
+        });
+
+        if ((JSON.stringify(containersArray) !== JSON.stringify(sentArray))) {
+            cardList = '';
+            newCards = '';
+            containersArray.forEach(container => {
+                const { container: containerName, state } = container;
+                const existingContainer = sentArray.find(c => c.container === containerName);
+                if (!existingContainer) {
+                    containerInfo(containerName).then(details => {
+                        createCard(details).then(card => {
+                            newCards += card;
+                        });
+                    });
+                    res.write(`event: update\n`);
+                    res.write(`data: 'update cards'\n\n`);
+                } else if (existingContainer.state !== state) {
+                    updatesArray.push(containerName);
+                }
+                containerInfo(containerName).then(details => {
+                    createCard(details).then(card => {
+                        cardList += card;
+                    });
+                });
+            });
+
+            sentArray.forEach(container => {
+                const { container: containerName } = container;
+                const existingContainer = containersArray.find(c => c.container === containerName);
+                if (!existingContainer) {
+                    updatesArray.push(containerName);
+                }
+            });
+
+            for (let i = 0; i < updatesArray.length; i++) {
+                res.write(`event: ${updatesArray[i]}\n`);
+                res.write(`data: 'update cards'\n\n`);
+            }
+            updatesArray = [];
+            sentArray = containersArray.slice();
+        }
+
+    }, 500);
+
+
+    req.on('close', () => {
+        clearInterval(eventCheck);
+    });
+};
+
+
 export const updateCards = async (req, res) => {
 export const updateCards = async (req, res) => {
     console.log('updateCards called');
     console.log('updateCards called');
     res.send(newCards);
     res.send(newCards);
@@ -152,8 +218,6 @@ export const Card = async (req, res) => {
 }
 }
 
 
 
 
-
-
 function status (state) {
 function status (state) {
     let status = `<span class="text-yellow align-items-center lh-1">
     let status = `<span class="text-yellow align-items-center lh-1">
                     <svg xmlns="http://www.w3.org/2000/svg" class="icon-tabler icon-tabler-point-filled" 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 7a5 5 0 1 1 -4.995 5.217l-.005 -.217l.005 -.217a5 5 0 0 1 4.995 -4.783z" stroke-width="0" fill="currentColor"></path></svg>
                     <svg xmlns="http://www.w3.org/2000/svg" class="icon-tabler icon-tabler-point-filled" 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 7a5 5 0 1 1 -4.995 5.217l-.005 -.217l.005 -.217a5 5 0 0 1 4.995 -4.783z" stroke-width="0" fill="currentColor"></path></svg>

+ 12 - 20
package-lock.json

@@ -13,13 +13,13 @@
         "dockerode": "^4.0.2",
         "dockerode": "^4.0.2",
         "dockerode-compose": "^1.4.0",
         "dockerode-compose": "^1.4.0",
         "ejs": "^3.1.9",
         "ejs": "^3.1.9",
-        "express": "^4.18.3",
+        "express": "^4.19.2",
         "express-session": "^1.18.0",
         "express-session": "^1.18.0",
         "js-yaml": "^4.1.0",
         "js-yaml": "^4.1.0",
         "memorystore": "^1.6.7",
         "memorystore": "^1.6.7",
         "sequelize": "^6.37.1",
         "sequelize": "^6.37.1",
         "sqlite3": "^5.1.7",
         "sqlite3": "^5.1.7",
-        "systeminformation": "^5.22.0"
+        "systeminformation": "^5.22.6"
       }
       }
     },
     },
     "node_modules/@balena/dockerignore": {
     "node_modules/@balena/dockerignore": {
@@ -525,9 +525,9 @@
       }
       }
     },
     },
     "node_modules/cookie": {
     "node_modules/cookie": {
-      "version": "0.5.0",
-      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
-      "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
+      "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
       "engines": {
       "engines": {
         "node": ">= 0.6"
         "node": ">= 0.6"
       }
       }
@@ -810,16 +810,16 @@
       }
       }
     },
     },
     "node_modules/express": {
     "node_modules/express": {
-      "version": "4.18.3",
-      "resolved": "https://registry.npmjs.org/express/-/express-4.18.3.tgz",
-      "integrity": "sha512-6VyCijWQ+9O7WuVMTRBTl+cjNNIzD5cY5mQ1WM8r/LEkI2u8EYpOotESNwzNlyCn3g+dmjKYI6BmNneSr/FSRw==",
+      "version": "4.19.2",
+      "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
+      "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
       "dependencies": {
       "dependencies": {
         "accepts": "~1.3.8",
         "accepts": "~1.3.8",
         "array-flatten": "1.1.1",
         "array-flatten": "1.1.1",
         "body-parser": "1.20.2",
         "body-parser": "1.20.2",
         "content-disposition": "0.5.4",
         "content-disposition": "0.5.4",
         "content-type": "~1.0.4",
         "content-type": "~1.0.4",
-        "cookie": "0.5.0",
+        "cookie": "0.6.0",
         "cookie-signature": "1.0.6",
         "cookie-signature": "1.0.6",
         "debug": "2.6.9",
         "debug": "2.6.9",
         "depd": "2.0.0",
         "depd": "2.0.0",
@@ -868,14 +868,6 @@
         "node": ">= 0.8.0"
         "node": ">= 0.8.0"
       }
       }
     },
     },
-    "node_modules/express-session/node_modules/cookie": {
-      "version": "0.6.0",
-      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
-      "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
     "node_modules/express-session/node_modules/cookie-signature": {
     "node_modules/express-session/node_modules/cookie-signature": {
       "version": "1.0.7",
       "version": "1.0.7",
       "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz",
       "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz",
@@ -2514,9 +2506,9 @@
       }
       }
     },
     },
     "node_modules/systeminformation": {
     "node_modules/systeminformation": {
-      "version": "5.22.0",
-      "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.22.0.tgz",
-      "integrity": "sha512-oAP80ymt8ssrAzjX8k3frbL7ys6AotqC35oikG6/SG15wBw+tG9nCk4oPaXIhEaAOAZ8XngxUv3ORq2IuR3r4Q==",
+      "version": "5.22.6",
+      "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.22.6.tgz",
+      "integrity": "sha512-hUTQX+bRgIFbv1T/z251NtwGwNIeSyWURnT2BGnsYu6dQNbkiBl4oAwk50acVfITFq1Zvb8KDNgibQK9uGlUGg==",
       "os": [
       "os": [
         "darwin",
         "darwin",
         "linux",
         "linux",

+ 2 - 2
package.json

@@ -16,12 +16,12 @@
     "dockerode": "^4.0.2",
     "dockerode": "^4.0.2",
     "dockerode-compose": "^1.4.0",
     "dockerode-compose": "^1.4.0",
     "ejs": "^3.1.9",
     "ejs": "^3.1.9",
-    "express": "^4.18.3",
+    "express": "^4.19.2",
     "express-session": "^1.18.0",
     "express-session": "^1.18.0",
     "js-yaml": "^4.1.0",
     "js-yaml": "^4.1.0",
     "memorystore": "^1.6.7",
     "memorystore": "^1.6.7",
     "sequelize": "^6.37.1",
     "sequelize": "^6.37.1",
     "sqlite3": "^5.1.7",
     "sqlite3": "^5.1.7",
-    "systeminformation": "^5.22.0"
+    "systeminformation": "^5.22.6"
   }
   }
 }
 }

+ 30 - 26
router/index.js

@@ -22,51 +22,55 @@ import { Portal } from "../controllers/portal.js"
 // Auth middleware
 // Auth middleware
 const auth = async (req, res, next) => {
 const auth = async (req, res, next) => {
     if (!req.session.user) { res.redirect('/login'); return; }
     if (!req.session.user) { res.redirect('/login'); return; }
+    if (req.session.role == "admin") { next(); }
 
 
     let user = req.session.user;
     let user = req.session.user;
     let role = req.session.role;
     let role = req.session.role;
     let action = req.path.split("/")[2];
     let action = req.path.split("/")[2];
     let trigger = req.header('hx-trigger-name');
     let trigger = req.header('hx-trigger-name');
+    
     // console.log("Auth: ", user, role, action, trigger);
     // console.log("Auth: ", user, role, action, trigger);
 
 
-    if (role == "admin") {
-        next();
-    }
-    else if (action == "start" || action == "stop" || action == "pause" || action == "restart") {
-        let permission = await Permission.findOne({ where: { containerName: trigger, user: user }, attributes: [`${action}`] });
+
+    // if (action == "start" || action == "stop" || action == "pause" || action == "restart") {
+    //     let permission = await Permission.findOne({ where: { containerName: trigger, user: user }, attributes: [`${action}`] });
         
         
-        if (permission) {
-            if (permission[action] == true) {
-                console.log(`User ${user} has permission to ${action} ${trigger}`);
-                next();
-            }
-            else {
-                console.log(`User ${user} does not have permission to ${action} ${trigger}`);
-            }
-        } else {
-            console.log(`No entry found for ${user} in ${trigger} permissions`);
-        }
-    }
-    else {
-        res.redirect('/portal');
-    }
+    //     if (permission) {
+    //         if (permission[action] == true) {
+    //             console.log(`User ${user} has permission to ${action} ${trigger}`);
+    //             next();
+    //         }
+    //         else {
+    //             console.log(`User ${user} does not have permission to ${action} ${trigger}`);
+    //         }
+    //     } else {
+    //         console.log(`No entry found for ${user} in ${trigger} permissions`);
+    //     }
+    // }
+    // else {
+    //     res.redirect('/portal');
+    // }
+
+    res.redirect('/portal');
 
 
 }
 }
 
 
+
+
+
 // Admin routes
 // Admin routes
 router.get("/", auth, Dashboard);
 router.get("/", auth, Dashboard);
 router.post("/action/:action", auth, Action);
 router.post("/action/:action", auth, Action);
 router.post("/updatePermissions", auth, UpdatePermissions);
 router.post("/updatePermissions", auth, UpdatePermissions);
 
 
-router.get("/logs", Logs);
-
-router.get("/modals", Modals);
+router.get("/logs", auth, Logs);
+router.get("/modals", auth, Modals);
 router.get("/stats", auth, Stats);
 router.get("/stats", auth, Stats);
 router.get("/chart", auth, Chart);
 router.get("/chart", auth, Chart);
 router.get("/sse_event", auth, SSE);
 router.get("/sse_event", auth, SSE);
-router.get("/containers", Containers);
-router.get("/card", Card);
-router.get("/new_cards", updateCards);
+router.get("/containers", auth, Containers);
+router.get("/card", auth, Card);
+router.get("/new_cards", auth, updateCards);
 
 
 
 
 router.get("/images", auth, Images);
 router.get("/images", auth, Images);

+ 10 - 0
views/partials/navbar.html

@@ -61,6 +61,16 @@
             VNC
             VNC
           </a>
           </a>
         </div> -->
         </div> -->
+
+        <!-- <% if(role == 'admin') { %>
+          <div class="btn-list">
+            <a href="#" class="btn text-red">
+              Admin
+            </a>
+          </div>
+        <% } %> -->
+
+
         
         
         
         
       </div>
       </div>

+ 6 - 5
views/portal.html

@@ -9,6 +9,7 @@
     <link href="/css/tabler.min.css" rel="stylesheet"/>
     <link href="/css/tabler.min.css" rel="stylesheet"/>
     <link href="/css/meters.css" rel="stylesheet"/>
     <link href="/css/meters.css" rel="stylesheet"/>
     <script src="/js/htmx.min.js"></script>
     <script src="/js/htmx.min.js"></script>
+    <script src="/js/htmx-sse.js"></script>
     <style>
     <style>
       @import url('/fonts/inter.css');
       @import url('/fonts/inter.css');
       :root {
       :root {
@@ -29,7 +30,7 @@
 
 
       <div class="page-body">
       <div class="page-body">
         <div class="container-xl">
         <div class="container-xl">
-          <div class="row row-deck row-cards">
+          <div class="row row-deck row-cards" hx-ext="sse" sse-connect="/sse_event">
             
             
             <div class="col-12">
             <div class="col-12">
               <div class="row row-cards">
               <div class="row row-cards">
@@ -45,7 +46,7 @@
                         </div>
                         </div>
 
 
                         <!-- HTMX -->
                         <!-- HTMX -->
-                        <div class="col" name="CPU" id="green" data-hx-get="" data-hx-trigger="">
+                        <div class="col" name="CPU" id="green" data-hx-get="/stats" data-hx-trigger="load, every 1s">
                           <div class="font-weight-medium">
                           <div class="font-weight-medium">
                             <label class="cpu-text mb-1" for="cpu">CPU 0%</label>
                             <label class="cpu-text mb-1" for="cpu">CPU 0%</label>
                           </div>
                           </div>
@@ -70,7 +71,7 @@
                         </div>
                         </div>
 
 
                         <!-- HTMX -->
                         <!-- HTMX -->
-                        <div class="col" name="RAM" id="blue" data-hx-get="" data-hx-trigger="">
+                        <div class="col" name="RAM" id="blue" data-hx-get="/stats" data-hx-trigger="load, every 2s">
                           <div class="font-weight-medium">
                           <div class="font-weight-medium">
                             <label class="ram-text mb-1" for="ram">RAM 0%</label>
                             <label class="ram-text mb-1" for="ram">RAM 0%</label>
                           </div>
                           </div>
@@ -95,7 +96,7 @@
                         </div>
                         </div>
 
 
                         <!-- HTMX -->
                         <!-- HTMX -->
-                        <div class="col" name="NET" id="purple" data-hx-get="" data-hx-trigger="">
+                        <div class="col" name="NET" id="purple" data-hx-get="/stats" data-hx-trigger="load, every 2s">
                           <div class="font-weight-medium">
                           <div class="font-weight-medium">
                             <label id="net-text" class="net-text mb-1" for="network">Down: 0MB  Up: 0MB</label>
                             <label id="net-text" class="net-text mb-1" for="network">Down: 0MB  Up: 0MB</label>
                           </div>
                           </div>
@@ -120,7 +121,7 @@
                         </div>
                         </div>
 
 
                         <!-- HTMX -->
                         <!-- HTMX -->
-                        <div class="col" name="DISK" id="orange" data-hx-get="" data-hx-trigger="">
+                        <div class="col" name="DISK" id="orange" data-hx-get="/stats" data-hx-trigger="load, every 3s">
                           <div class="font-weight-medium">
                           <div class="font-weight-medium">
                             <label class="disk-text mb-1" for="disk">DISK 0%</label>
                             <label class="disk-text mb-1" for="disk">DISK 0%</label>
                           </div>
                           </div>