Browse Source

Merge pull request #34 from lllllllillllllillll/dev

v0.08
lllllllillllllillll 1 year ago
parent
commit
bc10e26452

+ 13 - 0
CHANGELOG.md

@@ -1,3 +1,16 @@
+## v0.08 (Dec 15th 2023)
+* Updates to compose file and instructions from [steveiliop56](https://github.com/steveiliop56)
+* Added SECRET field to compose file as a basic security measure.
+* Visibility button to hide containers or reset view.
+* Container link now uses server IP address.
+* More compact container card, with style options planned.
+* Improved log view.
+* Removed VPN, Firewall, and VNC buttons.
+* Updated dependencies (Sequelize 6.35.2)
+* Fixed web pages not using the "public" static folder.
+* Small tweaks to router.
+* Replaced the default icon shown for missing icons (docker.png).
+
 ## v0.07 (Dec 8th 2023)
 * View container logs.
 * Removed Redis.

+ 0 - 1
Dockerfile

@@ -2,7 +2,6 @@
 
 FROM node:21-alpine
 
-ENV NODE_ENV production
 
 WORKDIR /app
 

BIN
DweebUI.png


+ 61 - 36
README.md

@@ -1,14 +1,16 @@
 # DweebUI
-DweebUI is a simple Docker web interface created with javascript and node.js
+DweebUI is a simple Docker web interface created using Javascript, Node.JS, and Express.
 
-Pre-Pre-Pre-Pre-Pre Alpha v0.07 ( :fire: Experimental. Don't install on any servers you care about :fire: )
+Pre-Pre-Pre-Pre-Pre Alpha v0.08 ( :fire: Experimental. Don't install on any servers you care about :fire: )
 
 [![GitHub Stars](https://img.shields.io/github/stars/lllllllillllllillll/DweebUI)](https://github.com/lllllllillllllillll)
-[![GitHub License](https://img.shields.io/github/license/lllllllillllllillll/DweebUI)](https://github.com/lllllllillllllillll/DweebUI/blob/main/LICENSE)
 [![GitHub Activity](https://img.shields.io/github/commit-activity/y/lllllllillllllillll/DweebUI)](https://github.com/lllllllillllllillll)
+[![Docker Pulls](https://img.shields.io/docker/pulls/lllllllillllllillll/dweebui)](https://hub.docker.com/repository/docker/lllllllillllllillll/dweebui)
+[![GitHub License](https://img.shields.io/github/license/lllllllillllllillll/DweebUI)](https://github.com/lllllllillllllillll/DweebUI/blob/main/LICENSE)
+
 
-* I haven't used Github very much and I'm still new to javascript.
-* This is the first project I've ever released and I'm sure it's full of plenty of bugs and mistakes.
+* This is a personal project that I decided to share. I'm sure it has plenty of bugs and mistakes.
+* I haven't used Github very much and I'm still new to Javascript.
 * I probably should have waited a lot longer to share this :|
 
 <a href="https://raw.githubusercontent.com//lllllllillllllillll/DweebUI/main/screenshots/dashboard.png"><img src="https://raw.githubusercontent.com/lllllllillllllillll/DweebUI/main/screenshots/dashboard.png" width="50%"/></a>
@@ -17,49 +19,71 @@ Pre-Pre-Pre-Pre-Pre Alpha v0.07 ( :fire: Experimental. Don't install on any serv
 
 
 ## Features
-* [x] Dashboard provides server metrics (cpu, ram, network, disk) and container controls on a single page.
+* [x] Dashboard provides server metrics, container metrics, and container controls, on a single page.
+* [x] View container logs.
+* [ ] Update containers (planned).
+* [ ] Manage your Docker networks, images, and volumes (planned).
 * [x] Light/Dark Mode.
 * [x] Easy to install app templates.
-* [x] Automatically persists data in docker volumes if bind mount isn't used. 
-* [x] Proxy manager for Caddy. (Optional)
-* [x] Partial Portainer Template Support (Network Mode, Ports, Volumes, Enviroment Variables, Labels, Commands, Restart Policy, Nvidia Hardware Acceleration).
+* [x] Proxy manager for Caddy (Optional).
 * [x] Multi-User built-in.
-* [ ] User pages: Shortcuts, Requests, Support. (planned)
+* [ ] User pages (planned).
 * [x] Support for Windows, Linux, and MacOS.
-* [ ] Import compose files. (planned)
-* [x] Javascript, Node.js, and Express.
+* [ ] Docker compose support (planned).
 * [x] Templates.json maintains compatability with Portainer, allowing you to use the template without needing to use DweebUI.
-* [ ] Manage your Docker networks, images, and volumes. (planned)
-* [ ] Preset variables. (planned)
-* [ ] VPN, VPS, and Firewall Toggles. (planned)
-* [ ] Offline Mode. (planned)
+* [x] Automatically persists data in docker volumes if bind mount isn't used.
+* [ ] Preset variables (planned).
+* [ ] Offline/Local Icons (planned).
+
 
 ## Setup
 
-* Docker compose.yaml: 
+Docker Compose: 
 ```
+version: "3.9"
 services:
-  dweebui:
-    container_name: DweebUI
-    image: lllllllillllllillll/dweebui:v0.07
-    environment:
-      NODE_ENV: production
-      PORT: 8000
-      # Proxy_Manager: enabled
-    restart: unless-stopped
-    ports:
-      - 8000:8000
-    volumes:
-      - dweebui:/app
-      - caddyfiles:/app/caddyfiles
-      - /var/run/docker.sock:/var/run/docker.sock
+
+  dweebui:
+    container_name: dweebui
+    image: lllllllillllllillll/dweebui:v0.08
+    # build:
+    #   context: .
+    environment:
+      NODE_ENV: production
+      PORT: 8000
+      SECRET: MrWiskers
+      #Proxy_Manager: enabled
+    restart: unless-stopped
+    ports:
+      - 8000:8000
+    volumes:
+      - dweebui:/app
+      - caddyfiles:/app/caddyfiles
+      - /var/run/docker.sock:/var/run/docker.sock
+      #- ./custom-templates.json:/app/custom-templates.json
+      #- ./composefiles:/app/composefiles
+    networks:
+      - dweeb_network
+
 
 volumes:
-  dweebui:
-  caddyfiles:
+  dweebui:
+  caddyfiles:
+
+
+networks:
+  dweeb_network:
+    driver: bridge
 ```
 
-* Using setup.sh: 
+Compose setup:
+
+* Paste the above content into a file named ```docker-compose.yml``` then place it in a folder named ```dweebui```.
+* Open a terminal in the ```dweebui``` folder, then enter ```docker compose up -d```.
+* You may need to use ```docker-compose up -d``` or execute the command as root with either ```sudo docker compose up -d``` or ```sudo docker-compose up -d```.
+
+
+Using setup.sh: 
 ```
 Extract DweebUI.zip and navigate to /DweebUI
 cd DweebUI
@@ -68,8 +92,9 @@ sudo ./setup.sh
 ```
 
 
-## Credit
+## Credits
 
+* Dockerode and dockerode-compose by Apocas: https://github.com/apocas/dockerode
 * UI was built using HTML and CSS elements from https://tabler.io/
 * Apps template based on Portainer template provided by Lissy93: https://github.com/Lissy93/portainer-templates
-* Icons from Walkxcode with some renames and additions: https://github.com/walkxcode/dashboard-icons
+* Icons from Walkxcode with some renames and additions: https://github.com/walkxcode/dashboard-icons

+ 33 - 2
app.js

@@ -8,10 +8,13 @@ const PORT = process.env.PORT || 8000;
 const routes = require("./routes");
 
 // Functions and variables
-const { serverStats, containerList, containerStats, containerAction, containerLogs } = require('./functions/system');
+const { serverStats, containerList, containerStats, containerAction, containerLogs, hiddenContainers } = require('./functions/system');
 let sentList, clicked;
 app.locals.site_list = '';
 
+const Containers = require('./database/ContainerSettings');
+
+
 // Configure Session
 const sessionMiddleware = session({
     secret: "keyboard cat", 
@@ -36,7 +39,7 @@ app.use([
 
 // Start Express server
 const server = app.listen(PORT, async () => {
-    console.log(`App listening on port ${PORT}`);   
+    console.log(`App listening on port ${PORT}`);
 });
 
 // Start Socket.io
@@ -90,11 +93,39 @@ io.on('connection', (socket) => {
         clicked = false;
     });
 
+    
+    socket.on('hide', async (data) => {
+        console.log(`Hide ${data.container}`);
+
+        let containerExists = await Containers.findOne({ where: {name:data.container}});
+        
+        if(!containerExists){
+            const container = await Containers.create({ 
+                name: data.container,
+                visibility: false
+             });
+             hiddenContainers();
+            console.log(`[Created] Container ${data.container} hidden`)
+        } else {
+            containerExists.update({ visibility: false });
+            console.log(`[Updated] Container ${data.container} hidden`)
+            hiddenContainers();
+        }
+    });
+
+    socket.on('reset', (data) => {
+        // set visibility to true for all containers
+        Containers.update({ visibility: true }, { where: {} });
+        console.log('All containers visible');
+        hiddenContainers();
+    });
+
 
     // Container logs
     socket.on('logs', (data) => {
         containerLogs(data.container)
         .then(logs => {
+            console.log(`Refreshed logs for ${data.container}`)
             socket.emit('logString', logs);
         })
         .catch(err => {

+ 34 - 12
components/dashCard.js

@@ -1,11 +1,24 @@
 module.exports.dashCard = function dashCard(data) {
   
-  let { name, service, id, state, image, external_port, internal_port, ports, volumes, environment_variables, labels } = data;
-  
+  let { name, service, id, state, image, external_port, internal_port, ports, volumes, environment_variables, labels, IPv4, style } = data;
+
+  let margin, iconSize, fontSize = '';
+
+  if (style == "Large") {
+    iconSize = 'width="150px"'
+  } else if ((style == "Compact") || (style == undefined)) {
+    iconSize = 'width="100px"'
+    margin = 'style="margin-bottom: 0;"'
+  } else if (style == "Row") {
+    iconSize = 'width="50px"'
+    margin = 'style="margin-bottom: 0;"'
+  }
+
+
   //disable controls for a docker container depending on its name
-  let enabled = "";
-  if (name.startsWith('Dweeb')) {
-    enabled = 'disabled=""';
+  let actions = "";
+  if (name.startsWith('dweebui')) {
+    actions = 'disabled=""';
   }
 
   if ( external_port == undefined ) { external_port = 0; }
@@ -115,23 +128,23 @@ module.exports.dashCard = function dashCard(data) {
       <div class="card">
         <div class="card-body">
           <div class="card-stamp card-stamp-sm">
-            <img heigh="150px" width="150px" src="https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/${service}.png" onerror="this.onerror=null;this.src='https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/dweebui.png';"></img>
+            <img ${iconSize} src="https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/${service}.png" onerror="this.onerror=null;this.src='https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/docker.png';"></img>
           </div>
           <div class="d-flex align-items-center">
             <div class="subheader text-yellow">${external_port}:${internal_port}</div>
             <div class="ms-auto lh-1">
               <div class="card-actions btn-actions">
                 <div class="card-actions btn-actions">
-                  <button onclick="buttonAction(this)" name="${name}" value="start" id="${state}" class="btn-action" title="Start" ${enabled}><!-- player-play -->
+                  <button onclick="buttonAction(this)" name="${name}" value="start" id="${state}" class="btn-action" title="Start" ${actions}><!-- player-play -->
                     <svg xmlns="http://www.w3.org/2000/svg" class="icon-tabler icon-tabler-player-play" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M7 4v16l13 -8z"></path></svg>
                   </button>
-                  <button onclick="buttonAction(this)" name="${name}" value="stop" id="${state}" class="btn-action" title="Stop" ${enabled}><!-- player-stop -->
+                  <button onclick="buttonAction(this)" name="${name}" value="stop" id="${state}" class="btn-action" title="Stop" ${actions}><!-- player-stop -->
                     <svg xmlns="http://www.w3.org/2000/svg" class="icon-tabler icon-tabler-player-stop" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M5 5m0 2a2 2 0 0 1 2 -2h10a2 2 0 0 1 2 2v10a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2 -2z"></path></svg>
                   </button>
-                  <button onclick="buttonAction(this)" name="${name}" value="pause" id="${state}" class="btn-action" title="Pause" ${enabled}><!-- player-pause -->
+                  <button onclick="buttonAction(this)" name="${name}" value="pause" id="${state}" class="btn-action" title="Pause" ${actions}><!-- player-pause -->
                     <svg xmlns="http://www.w3.org/2000/svg" class="icon-tabler icon-tabler-player-pause" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M6 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z"></path><path d="M14 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z"></path></svg>
                   </button>
-                  <button onclick="buttonAction(this)" name="${name}" value="restart" id="${state}" class="btn-action" title="Restart" ${enabled}><!-- reload -->
+                  <button onclick="buttonAction(this)" name="${name}" value="restart" id="${state}" class="btn-action" title="Restart" ${actions}><!-- reload -->
                     <svg xmlns="http://www.w3.org/2000/svg" class="icon-tabler icon-tabler-reload" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M19.933 13.041a8 8 0 1 1 -9.925 -8.788c3.899 -1 7.935 1.007 9.425 4.747"></path><path d="M20 4v5h-5"></path></svg>                          
                   </button>
                   <div class="dropdown">
@@ -146,13 +159,22 @@ module.exports.dashCard = function dashCard(data) {
                       <a class="dropdown-item text-danger" data-bs-toggle="modal" data-bs-target="#${name}_modal-danger" href="#">Remove</a>
                     </div>
                   </div>
+                  <div class="dropdown">
+                    <a href="#" class="btn-action dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><!-- Download SVG icon from http://tabler-icons.io/i/dots-vertical -->
+                      <svg xmlns="http://www.w3.org/2000/svg" class="icon-tabler icon-tabler-eye" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"/> <path d="M10 12a2 2 0 1 0 4 0a2 2 0 0 0 -4 0" /> <path d="M21 12c-2.4 4 -5.4 6 -9 6c-3.6 0 -6.6 -2 -9 -6c2.4 -4 5.4 -6 9 -6c3.6 0 6.6 2 9 6" /> </svg>
+                    </a>
+                    <div class="dropdown-menu dropdown-menu-end">
+                      <a class="dropdown-item" onclick="hideContainer(this)" name="${name}" href="#">Hide</a>
+                      <a class="dropdown-item" onclick="resetView()" name="${name}" href="#">Reset View</a>
+                    </div>
+                  </div>
                 </div>
               </div>
             </div>
           </div>
           <div class="d-flex align-items-baseline">
-            <div class="h1 me-2" title="${name}">
-              <a href="http://localhost:${external_port}" target="_blank">
+            <div class="h1 me-2" title="${name}" ${margin}>
+              <a href="http://${IPv4}:${external_port}" target="_blank">
                 ${shortened_name}
               </a>
             </div>

+ 0 - 19
compose.yaml

@@ -1,19 +0,0 @@
-services:
-  dweebui:
-    container_name: DweebUI
-    image: lllllllillllllillll/dweebui:v0.07
-    environment:
-      NODE_ENV: production
-      PORT: 8000
-      # Proxy_Manager: enabled
-    restart: unless-stopped
-    ports:
-      - 8000:8000
-    volumes:
-      - dweebui:/app
-      - caddyfiles:/app/caddyfiles
-      - /var/run/docker.sock:/var/run/docker.sock
-
-volumes:
-  dweebui:
-  caddyfiles:

+ 6 - 8
controllers/apps.js

@@ -1,11 +1,8 @@
 const User = require('../database/UserModel');
 const { appCard } = require('../components/appCard')
 const { dashCard } = require('../components/dashCard');
-
 const { install, uninstall } = require('../functions/package_manager');
 
-// import { install, uninstall } from '../functions/package_manager';
-
 const templates_json = require('../templates.json');
 let templates = templates_json.templates;
 
@@ -18,23 +15,24 @@ templates = templates.sort((a, b) => {
   
 
 exports.Apps = async function(req, res) {
+
     if (req.session.role == "admin") {
 
         // Get the user.
         let user = await User.findOne({ where: { UUID: req.session.UUID }});
 
-        let page = Number(req.query.page) || 1;
+        let page = Number(req.params.page) || 1;
         let list_start = (page - 1) * 28;
         let list_end = (page * 28);
         let last_page = Math.ceil(templates.length / 28);
 
-        let prev = '/apps?page=' + (page - 1);
-        let next = '/apps?page=' + (page + 1);
+        let prev = '/apps/' + (page - 1);
+        let next = '/apps/' + (page + 1);
         if (page == 1) {
-            prev = '/apps?page=' + (page);
+            prev = '/apps/' + (page);
         }
         if (page == last_page) {
-            next = '/apps?page=' + (page);
+            next = '/apps/' + (page);
         }
 
         let apps_list = '';

+ 2 - 2
controllers/auth.js

@@ -86,11 +86,11 @@ exports.Register = function(req,res){
 exports.processRegister = async function(req,res){
 
     // Get the data.
-    let { first_name, last_name, username, email, password, avatar, tos } = req.body;
+    let { first_name, last_name, username, email, password, avatar, tos, secret } = req.body;
     let role = "user";
 
     // Check the data.
-    if(first_name && last_name && email && password && username && tos){
+    if((first_name && last_name && email && password && username && tos) && (secret == process.env.SECRET)){
 
         // Check if there is an existing user with that username.
         let existingUser = await User.findOne({ where: {username:username}});

+ 2 - 0
controllers/dashboard.js

@@ -1,4 +1,6 @@
 const User = require('../database/UserModel');
+const Containers = require('../database/ContainerSettings');
+
 const { readFileSync, writeFileSync, appendFileSync, readdirSync } = require('fs');
 const { execSync } = require("child_process");
 const { siteCard } = require('../components/siteCard');

+ 47 - 0
database/ContainerSettings.js

@@ -0,0 +1,47 @@
+const { Sequelize, DataTypes } = require('sequelize');
+
+const sequelize = new Sequelize({
+  dialect: 'sqlite',
+  storage: './database/db.sqlite',
+  logging: false
+});
+
+
+const Containers = sequelize.define('Containers', {
+  // Model attributes are defined here
+  id: {
+    type: DataTypes.INTEGER,
+    autoIncrement: true,
+    primaryKey: true
+  },
+  name: {
+    type: DataTypes.STRING,
+    allowNull: false
+  },
+  visibility: {
+    type: DataTypes.STRING
+    // allowNull defaults to true
+  },
+  size: {
+    type: DataTypes.STRING
+    // allowNull defaults to true
+  },
+  group: {
+    type: DataTypes.STRING
+    // allowNull defaults to true
+  },
+  permissions: {
+    type: DataTypes.STRING
+    // allowNull defaults to true
+  }
+});
+
+async function syncModel() {
+  await sequelize.sync();
+  console.log('Containers model synced');
+}
+
+syncModel();
+
+
+module.exports = Containers;

+ 30 - 0
docker-compose.yaml

@@ -0,0 +1,30 @@
+version: "3.9"
+services:
+  dweebui:
+    container_name: dweebui
+    image: lllllllillllllillll/dweebui:v0.08
+    # build:
+    #   context: .
+    environment:
+      PORT: 8000
+      SECRET: MrWiskers
+      #Proxy_Manager: enabled
+    restart: unless-stopped
+    ports:
+      - 8000:8000
+    volumes:
+      - dweebui:/app
+      - caddyfiles:/app/caddyfiles
+      - /var/run/docker.sock:/var/run/docker.sock
+      #- ./custom-templates.json:/app/custom-templates.json
+      #- ./composefiles:/app/composefiles
+    networks:
+      - dweeb_network
+
+volumes:
+  dweebui:
+  caddyfiles:
+
+networks:
+  dweeb_network:
+    driver: bridge

+ 1 - 6
functions/package_manager.js

@@ -1,7 +1,7 @@
 const { writeFileSync, mkdirSync, readFileSync } = require("fs");
 const yaml = require('js-yaml');
 
-const { exec, execSync } = require("child_process");
+const { execSync } = require("child_process");
 
 const { docker } = require('./system');
 
@@ -18,11 +18,6 @@ module.exports.install = async function (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 ((service_name.includes('caddy')) || (name.includes('caddy'))) {
-            req.app.locals.caddy = 'enabled';
-        }
-
         let docker_volumes = [];
 
         if (image.startsWith('https://')){

+ 25 - 5
functions/system.js

@@ -1,12 +1,26 @@
-const { currentLoad, mem, networkStats, fsSize, dockerContainerStats } = require('systeminformation');
+const { currentLoad, mem, networkStats, fsSize, dockerContainerStats, networkInterfaces } = require('systeminformation');
 var Docker = require('dockerode');
 var docker = new Docker({ socketPath: '/var/run/docker.sock' });
 const { dashCard } = require('../components/dashCard');
 const { Readable } = require('stream');
 
+const Containers = require('../database/ContainerSettings');
+
 // export docker
 module.exports.docker = docker;
 
+
+let IPv4 = '';
+networkInterfaces().then(data => {
+    IPv4 = data[0].ip4;
+});
+
+let hidden = '';
+module.exports.hiddenContainers = async function () {
+    hidden = await Containers.findAll({ where: {visibility:false}});
+    hidden = hidden.map(a => a.name);
+}
+
 module.exports.serverStats = async function () {
     const cpuUsage = await currentLoad();
     const ramUsage = await mem();
@@ -33,7 +47,7 @@ module.exports.containerList = async function () {
     for (const container of data) {
 
 
-        if ((container.Names[0].slice(1) != 'DweebUI') && (container.Names[0].slice(1) != 'DweebCache')) {
+        if (!hidden.includes(container.Names[0].slice(1))) {
 
             let imageVersion = container.Image.split('/');
             let service = imageVersion[imageVersion.length - 1].split(':')[0];
@@ -53,7 +67,9 @@ module.exports.containerList = async function () {
                     }
                     ports_list.push(ports);
                 }
-            } catch { console.log('no ports') }
+            } catch { 
+                // console.log('no ports') 
+                }
 
             for (let i = 0; i < 12; i++) {
                 if (ports_list[i] == undefined) {
@@ -78,7 +94,9 @@ module.exports.containerList = async function () {
                         readwrite: value.split(':')[2]
                     }
                     volumes_list.push(volumes);
-                }} catch { console.log('no volumes') }
+                }} catch { 
+                    // console.log('no volumes') 
+                    }
             for (let i = 0; i < 12; i++) {
                 if (volumes_list[i] == undefined) {
                     let volumes = {
@@ -147,6 +165,8 @@ module.exports.containerList = async function () {
                 volumes: volumes_list,
                 environment_variables: environment_variables,
                 labels: labels,
+                IPv4: IPv4,
+                style: "Compact"
             }
 
             let dockerCard = dashCard(container_info);
@@ -172,7 +192,7 @@ module.exports.containerStats = async function () {
 
     for (const container of data) {
 
-        if ((container.Names[0].slice(1) != 'DweebUI') && (container.Names[0].slice(1) != 'DweebCache')) {
+        if (!hidden.includes(container.Names[0].slice(1))) {
             const stats = await dockerContainerStats(container.Id);
             
             let container_stat = {

+ 9 - 9
package-lock.json

@@ -1,11 +1,11 @@
 {
-  "name": "dweeb-ui",
+  "name": "dweebui",
   "version": "1.0.0",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
-      "name": "dweeb-ui",
+      "name": "dweebui",
       "version": "1.0.0",
       "license": "ISC",
       "dependencies": {
@@ -17,7 +17,7 @@
         "express": "^4.18.2",
         "express-session": "^1.17.3",
         "js-yaml": "^4.1.0",
-        "sequelize": "^6.35.1",
+        "sequelize": "^6.35.2",
         "socket.io": "^4.6.1",
         "sqlite3": "^5.1.6",
         "systeminformation": "^5.21.20"
@@ -118,9 +118,9 @@
       "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g=="
     },
     "node_modules/@types/node": {
-      "version": "20.10.3",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.3.tgz",
-      "integrity": "sha512-XJavIpZqiXID5Yxnxv3RUDKTN5b81ddNC3ecsA0SoFXz/QU8OGBwZGMomiq0zw+uuqbL/krztv/DINAQ/EV4gg==",
+      "version": "20.10.4",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.4.tgz",
+      "integrity": "sha512-D08YG6rr8X90YB56tSIuBaddy/UXAA9RKJoFvrsnogAum/0pmjkgi4+2nx96A330FmioegBWmEYQ+syqCFaveg==",
       "dependencies": {
         "undici-types": "~5.26.4"
       }
@@ -1999,9 +1999,9 @@
       "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
     },
     "node_modules/sequelize": {
-      "version": "6.35.1",
-      "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.35.1.tgz",
-      "integrity": "sha512-UlP5k33nJsN11wCDLaWZXw9bB8w4ESKc5QmG6D04qMimwBwKVNeqRJiaaBlEJdtg8cRK+OJh95dliP+uEi+g9Q==",
+      "version": "6.35.2",
+      "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.35.2.tgz",
+      "integrity": "sha512-EdzLaw2kK4/aOnWQ7ed/qh3B6/g+1DvmeXr66RwbcqSm/+QRS9X0LDI5INBibsy4eNJHWIRPo3+QK0zL+IPBHg==",
       "funding": [
         {
           "type": "opencollective",

+ 2 - 2
package.json

@@ -1,5 +1,5 @@
 {
-  "name": "dweeb-ui",
+  "name": "dweebui",
   "version": "1.0.0",
   "main": "app.js",
   "keywords": [],
@@ -14,7 +14,7 @@
     "express": "^4.18.2",
     "express-session": "^1.17.3",
     "js-yaml": "^4.1.0",
-    "sequelize": "^6.35.1",
+    "sequelize": "^6.35.2",
     "socket.io": "^4.6.1",
     "sqlite3": "^5.1.6",
     "systeminformation": "^5.21.20"

+ 8 - 0
public/js/main.js

@@ -116,6 +116,14 @@ function buttonAction(button) {
 }
 
 
+function hideContainer(button) {
+  socket.emit('hide', {container: button.name});
+}
+
+function resetView() {
+  socket.emit('reset');
+}
+
 let containerLogs;
 
 function viewLogs(button) {

+ 17 - 15
routes/index.js

@@ -3,14 +3,14 @@ const router = express.Router();
 
 const { Dashboard, AddSite, RemoveSite, RefreshSites, DisableSite, EnableSite } = require("../controllers/dashboard");
 const { Login, processLogin, Logout, Register, processRegister } = require("../controllers/auth");
-
 const { Apps, searchApps, Install, Uninstall } = require("../controllers/apps");
+
 const { Users } = require("../controllers/users");
 const { Account } = require("../controllers/account");
 const { Settings } = require("../controllers/settings");
 
 
-
+// Dashboard
 router.get("/", Dashboard);
 router.post("/addsite", AddSite)
 router.post("/removesite", RemoveSite)
@@ -18,29 +18,31 @@ router.get("/refreshsites", RefreshSites)
 router.post("/disablesite", DisableSite)
 router.post("/enablesite", EnableSite)
 
+// Auth
+router.get("/login",Login);
+router.post("/login",processLogin);
+router.get("/register", Register);
+router.post("/register",processRegister);
+router.get("/logout",Logout);
 
-router.post("/install", Install)
-router.post("/uninstall", Uninstall)
-
-
-
-router.get("/users", Users);
-
+// Apps page
 router.get("/apps", Apps);
+router.get("/apps/:page", Apps);
+router.get("/apps/:template/:page", Apps);
 router.post("/apps", searchApps);
 
+
+
+// Settings page
 router.get("/settings", Settings);
 router.get("/account", Account);
 
-router.get("/login",Login); // Login page
-router.post("/login",processLogin); // Process login
 
-router.get("/register", Register); // Register page
-router.post("/register",processRegister); // Process Register
-
-router.get("/logout",Logout); // Logout
 
+router.post("/install", Install)
+router.post("/uninstall", Uninstall)
 
+router.get("/users", Users);
 
 
 module.exports = router;

BIN
screenshots/logs.png


+ 5 - 5
views/pages/account.ejs

@@ -6,8 +6,8 @@
 		<meta http-equiv="X-UA-Compatible" content="ie=edge"/>
 		<title>Settings - Tabler - Premium and Open Source dashboard template with responsive and high quality UI.</title>
 		<!-- CSS files -->
-		<link href="./css/tabler.min.css?1684106062" rel="stylesheet"/>
-		<link href="./css/demo.min.css?1684106062" rel="stylesheet"/>
+		<link href="/css/tabler.min.css?1684106062" rel="stylesheet"/>
+		<link href="/css/demo.min.css?1684106062" rel="stylesheet"/>
 		<style>
 		@import url('https://rsms.me/inter/inter.css');
 		:root {
@@ -19,7 +19,7 @@
 		</style>
 	</head>
 	<body >
-		<script src="./js/demo-theme.min.js?1684106062"></script>
+		<script src="/js/demo-theme.min.js?1684106062"></script>
 		<div class="page">
 		<!-- Navbar -->
 		<%- include('../partials/navbar.ejs') %>
@@ -139,7 +139,7 @@
 		</div>
 		<!-- Libs JS -->
 		<!-- Tabler Core -->
-		<script src="./js/tabler.min.js?1684106062" defer></script>
-		<script src="./js/demo.min.js?1684106062" defer></script>
+		<script src="/js/tabler.min.js?1684106062" defer></script>
+		<script src="/js/demo.min.js?1684106062" defer></script>
 	</body>
 	</html>

+ 25 - 7
views/pages/apps.ejs

@@ -6,8 +6,8 @@
     <meta http-equiv="X-UA-Compatible" content="ie=edge"/>
     <title>Apps list.</title>
     <!-- CSS files -->
-    <link href="./css/tabler.min.css?1685973381" rel="stylesheet"/>
-    <link href="./css/demo.min.css?1685973381" rel="stylesheet"/>
+    <link href="/css/tabler.min.css?1685973381" rel="stylesheet"/>
+    <link href="/css/demo.min.css?1685973381" rel="stylesheet"/>
     <style>
       @import url('https://rsms.me/inter/inter.css');
       :root {
@@ -19,7 +19,7 @@
     </style>
   </head>
   <body >
-    <script src="./js/demo-theme.min.js?1685973381"></script>
+    <script src="/js/demo-theme.min.js?1685973381"></script>
     <div class="page">
       <!-- Navbar -->
 
@@ -38,13 +38,31 @@
               </div>
               <!-- Page title actions -->
               <div class="col-auto ms-auto d-print-none">
+
                 <div class="d-flex">
                   <form action="/apps" id="search" name="search" method="POST">
-                    <input type="search" class="form-control" name="search" placeholder="Search apps…"/>
+                    <input type="search" class="form-control" name="search" placeholder="Search apps…">
                   </form>
-                   <input type="submit" form="search" class="btn btn-outline-success h-50" value="search"/>
+                  &nbsp;<input type="submit" form="search" class="btn btn-outline-success h-50" value="search">
+
+                  <div class="card-actions btn-actions">
+                    <div class="card-actions btn-actions">
+                      <div class="dropdown">
+                        <a href="#" class="btn-action dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" title="Change Templates">
+                          <svg xmlns="http://www.w3.org/2000/svg" class="icon-tabler icon-tabler-settings" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M10.325 4.317c.426 -1.756 2.924 -1.756 3.35 0a1.724 1.724 0 0 0 2.573 1.066c1.543 -.94 3.31 .826 2.37 2.37a1.724 1.724 0 0 0 1.065 2.572c1.756 .426 1.756 2.924 0 3.35a1.724 1.724 0 0 0 -1.066 2.573c.94 1.543 -.826 3.31 -2.37 2.37a1.724 1.724 0 0 0 -2.572 1.065c-.426 1.756 -2.924 1.756 -3.35 0a1.724 1.724 0 0 0 -2.573 -1.066c-1.543 .94 -3.31 -.826 -2.37 -2.37a1.724 1.724 0 0 0 -1.065 -2.572c-1.756 -.426 -1.756 -2.924 0 -3.35a1.724 1.724 0 0 0 1.066 -2.573c-.94 -1.543 .826 -3.31 2.37 -2.37c1 .608 2.296 .07 2.572 -1.065z" /><path d="M9 12a3 3 0 1 0 6 0a3 3 0 0 0 -6 0" /></svg>
+                        </a>
+                        <div class="dropdown-menu dropdown-menu-end">
+                          <a class="dropdown-item" href="#">Default Template</a>
+                          <a class="dropdown-item" href="#">Compose Files</a>
+                          <a class="dropdown-item" href="#">Custom Template</a>
+                        </div>
+                      </div>
+                    </div>
+                  </div>
+
                 </div>
               </div>
+
             </div>
           </div>
         </div>
@@ -87,7 +105,7 @@
     </div>
     <!-- Libs JS -->
     <!-- Tabler Core -->
-    <script src="./js/tabler.min.js?1685973381" defer></script>
-    <script src="./js/demo.min.js?1685973381" defer></script>
+    <script src="/js/tabler.min.js?1685973381" defer></script>
+    <script src="/js/demo.min.js?1685973381" defer></script>
   </body>
 </html>

+ 7 - 10
views/pages/dashboard.ejs

@@ -6,9 +6,9 @@
     <meta http-equiv="X-UA-Compatible" content="ie=edge"/>
     <title>Dashboard.</title>
     <!-- CSS files -->
-    <link href="./css/tabler.min.css?1684106062" rel="stylesheet"/>
-    <link href="./css/demo.min.css?1684106062" rel="stylesheet"/>
-    <link href="./css/meters.css" rel="stylesheet"/>
+    <link href="/css/tabler.min.css?1684106062" rel="stylesheet"/>
+    <link href="/css/demo.min.css?1684106062" rel="stylesheet"/>
+    <link href="/css/meters.css" rel="stylesheet"/>
     <style>
       @import url('https://rsms.me/inter/inter.css');
       :root {
@@ -137,9 +137,6 @@
                     <div class="modal-body">
                       
                     <div class="card-body">
-                          <h4>
-                            Log File:
-                          </h4>
                           <h4>Logs:</h4>
                           <div id="logView">
                             <pre>No logs available</pre>
@@ -297,13 +294,13 @@
     
 
     <!-- Libs JS -->
-    <script src="./libs/apexcharts/dist/apexcharts.min.js?1684106062" defer></script>
+    <script src="/libs/apexcharts/dist/apexcharts.min.js?1684106062" defer></script>
     <!-- Tabler Core -->
-    <script src="./js/tabler.min.js?1684106062" defer></script>
-    <script src="./js/demo.min.js?1684106062" defer></script>
+    <script src="/js/tabler.min.js?1684106062" defer></script>
+    <script src="/js/demo.min.js?1684106062" defer></script>
     <!-- Socket.io -->
     <script src="/socket.io/socket.io.js"></script>
-    <script src="./js/main.js"></script>
+    <script src="/js/main.js"></script>
     
   </body>
 </html>

+ 5 - 5
views/pages/login.ejs

@@ -6,8 +6,8 @@
     <meta http-equiv="X-UA-Compatible" content="ie=edge"/>
     <title>DweebUI - Login</title>
     <!-- CSS files -->
-    <link href="./css/tabler.min.css?1674944402" rel="stylesheet"/>
-    <link href="./css/demo.min.css?1674944402" rel="stylesheet"/>
+    <link href="/css/tabler.min.css?1674944402" rel="stylesheet"/>
+    <link href="/css/demo.min.css?1674944402" rel="stylesheet"/>
     <style>
       @import url('https://rsms.me/inter/inter.css');
       :root {
@@ -19,7 +19,7 @@
     </style>
   </head>
   <body  class=" d-flex flex-column">
-    <script src="./js/demo-theme.js?1674944402"></script>
+    <script src="/js/demo-theme.js?1674944402"></script>
     <div class="page page-center">
       <div class="container container-tight py-4">
         <div class="text-center mb-4">
@@ -75,7 +75,7 @@
     </div>
     <!-- Libs JS -->
     <!-- Tabler Core -->
-    <script src="./js/tabler.min.js?1674944402" defer></script>
-    <script src="./js/demo.min.js?1674944402" defer></script>
+    <script src="/js/tabler.min.js?1674944402" defer></script>
+    <script src="/js/demo.min.js?1674944402" defer></script>
   </body>
 </html>

+ 10 - 5
views/pages/register.ejs

@@ -6,8 +6,8 @@
     <meta http-equiv="X-UA-Compatible" content="ie=edge"/>
     <title>DweebUI - Register</title>
     <!-- CSS files -->
-    <link href="./css/tabler.min.css?1684106062" rel="stylesheet"/>
-    <link href="./css/demo.min.css?1684106062" rel="stylesheet"/>
+    <link href="/css/tabler.min.css?1684106062" rel="stylesheet"/>
+    <link href="/css/demo.min.css?1684106062" rel="stylesheet"/>
     <style>
       @import url('https://rsms.me/inter/inter.css');
       :root {
@@ -19,7 +19,7 @@
     </style>
   </head>
   <body  class=" d-flex flex-column">
-    <script src="./js/demo-theme.js?1684106062"></script>
+    <script src="/js/demo-theme.js?1684106062"></script>
     <div class="page page-center">
 
       
@@ -79,6 +79,11 @@
               </div>
             </div>
 
+            <div class="mb-2">
+              <label class="form-label">SECRET</label>
+              <input type="text" class="form-control" id="secret" name="secret" title="Enter the value of 'SECRET' from the DweebUI docker-compose.yaml">
+            </div>
+
             <label class="form-label">Avatar</label>
             <div class="mb-2">
               <div class="row g-2">
@@ -176,7 +181,7 @@
     </div>
     <!-- Libs JS -->
     <!-- Tabler Core -->
-    <script src="./js/tabler.min.js?1684106062" defer></script>
-    <script src="./js/demo.min.js?1684106062" defer></script>
+    <script src="/js/tabler.min.js?1684106062" defer></script>
+    <script src="/js/demo.min.js?1684106062" defer></script>
   </body>
 </html>

+ 5 - 5
views/pages/settings.ejs

@@ -14,8 +14,8 @@
 		<meta http-equiv="X-UA-Compatible" content="ie=edge"/>
 		<title>Settings</title>
 		<!-- CSS files -->
-		<link href="./css/tabler.min.css?1684106062" rel="stylesheet"/>
-		<link href="./css/demo.min.css?1684106062" rel="stylesheet"/>
+		<link href="/css/tabler.min.css?1684106062" rel="stylesheet"/>
+		<link href="/css/demo.min.css?1684106062" rel="stylesheet"/>
 		<style>
 		@import url('https://rsms.me/inter/inter.css');
 		:root {
@@ -27,7 +27,7 @@
 		</style>
 	</head>
 	<body >
-		<script src="./js/demo-theme.min.js?1684106062"></script>
+		<script src="/js/demo-theme.min.js?1684106062"></script>
 		<div class="page">
 		<!-- Navbar -->
 		<%- include('../partials/navbar.ejs') %>
@@ -139,7 +139,7 @@
 		</div>
 		<!-- Libs JS -->
 		<!-- Tabler Core -->
-		<script src="./js/tabler.min.js?1684106062" defer></script>
-		<script src="./js/demo.min.js?1684106062" defer></script>
+		<script src="/js/tabler.min.js?1684106062" defer></script>
+		<script src="/js/demo.min.js?1684106062" defer></script>
 	</body>
 	</html>

+ 5 - 5
views/pages/users.ejs

@@ -14,8 +14,8 @@
     <meta http-equiv="X-UA-Compatible" content="ie=edge"/>
     <title>Users</title>
     <!-- CSS files -->
-    <link href="./css/tabler.min.css?1685973381" rel="stylesheet"/>
-    <link href="./css/demo.min.css?1685973381" rel="stylesheet"/>
+    <link href="/css/tabler.min.css?1685973381" rel="stylesheet"/>
+    <link href="/css/demo.min.css?1685973381" rel="stylesheet"/>
     <style>
       @import url('https://rsms.me/inter/inter.css');
       :root {
@@ -27,7 +27,7 @@
     </style>
   </head>
   <body >
-    <script src="./js/demo-theme.min.js?1685973381"></script>
+    <script src="/js/demo-theme.min.js?1685973381"></script>
     <div class="page">
       <!-- Navbar -->
       
@@ -75,7 +75,7 @@
     </div>
     <!-- Libs JS -->
     <!-- Tabler Core -->
-    <script src="./js/tabler.min.js?1685973381" defer></script>
-    <script src="./js/demo.min.js?1685973381" defer></script>
+    <script src="/js/tabler.min.js?1685973381" defer></script>
+    <script src="/js/demo.min.js?1685973381" defer></script>
   </body>
 </html>

+ 1 - 1
views/partials/footer.ejs

@@ -24,7 +24,7 @@
           </li>
           <li class="list-inline-item">
             <a href="#" class="link-secondary" rel="noopener">
-              v0.07
+              v0.08
             </a>
           </li>
         </ul>

+ 7 - 6
views/partials/navbar.ejs

@@ -53,28 +53,29 @@
     </button>
     <h1 class="navbar-brand navbar-brand-autodark d-none-navbar-horizontal pe-0 pe-md-3">
       <a href="#">
-        <img src="./static/logo.svg" width="110" height="32" alt="Tabler" class="navbar-brand-image">
+        <img src="/static/logo.svg" width="110" height="32" alt="Tabler" class="navbar-brand-image">
       </a>
     </h1>
     <div class="navbar-nav flex-row order-md-last">
       <div class="nav-item d-none d-md-flex me-3">
-        <div class="btn-list">
+
+
+        <!-- <div class="btn-list">
           <a href="#" class="btn text-green">
-            <!-- 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
           </a>
           <a href="#" class="btn text-green">
-            <!-- Download SVG icon from http://tabler-icons.io/i/shield -->
             <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-green">
-            <!-- 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
           </a>
-        </div>
+        </div> -->
+        
+        
       </div>
       <div class="d-none d-md-flex">
 

+ 0 - 0
views/partials/site_list.ejs


+ 0 - 12
views/partials/users_list.ejs

@@ -1,12 +0,0 @@
-<tr>
-  <td><input class="form-check-input" type="checkbox"></td>
-  <td>1</td>
-  <td><span class="avatar me-2" style="background-image: url(./static/avatars/burns.jpg)"></span></td>
-  <td>John Doe</td>
-  <td>JDoe</td>
-  <td>JDoe@gmail.com</td>
-  <td>685468468465138</td>
-  <td>Admin</td>
-  <td><span class="badge badge-outline text-green">Active</span></td>
-  <td><a href="#" class="btn">Edit</a></td>
-</tr>