Bläddra i källkod

Another big rewrite

lllllllillllllillll 1 år sedan
förälder
incheckning
0f5575075e

+ 5 - 0
.dockerignore

@@ -0,0 +1,5 @@
+**/db.sqlite
+**/node_modules
+**/appdata
+.gitignore
+**/screenshots

+ 3 - 8
.gitignore

@@ -1,8 +1,3 @@
-**/node_modules/
-**/database.sqlite
-**/appdata/
-.github
-test
-.dockerignore
-.gitignore
-docker-compose.yaml
+**/db.sqlite
+**/node_modules
+**/appdata

+ 8 - 0
CHANGELOG.md

@@ -1,3 +1,11 @@
+## v0.21 (dev) - Another rewrite
+* Rewrote the dashboard to use HTMX.
+* Removed Socket.io.
+* Views are now HTML instead of EJS.
+* Improved Dockerfile.
+* Express sessions configured to use memorystore.
+* 
+
 ## v0.20 (Jan 20th 2024) - The rewrite. Jumping all the way to v0.20.
 ## v0.20 (Jan 20th 2024) - The rewrite. Jumping all the way to v0.20.
 * Changed to ES6 imports.
 * Changed to ES6 imports.
 * Cleaned up file structure and code layout.
 * Cleaned up file structure and code layout.

+ 10 - 12
Dockerfile

@@ -1,19 +1,17 @@
 FROM node:21-alpine
 FROM node:21-alpine
-
 ENV NODE_ENV=production
 ENV NODE_ENV=production
 
 
-WORKDIR /app
-
-RUN --mount=type=bind,source=package.json,target=package.json \
-    --mount=type=bind,source=package-lock.json,target=package-lock.json \
-    --mount=type=cache,target=/root/.npm \
-    npm ci --omit=dev
+ARG TARGETPLATFORM
+ARG BUILDPLATFORM
+RUN echo "I am running on $BUILDPLATFORM, building for $TARGETPLATFORM" > /log
 
 
+WORKDIR /app
 
 
-USER root
-
-COPY . .
+RUN chown node:node /app
+USER node
 
 
+COPY package.json package-lock.json* /app/
+RUN npm ci && npm cache clean --force
+COPY . /app
 EXPOSE 8000
 EXPOSE 8000
-
-CMD ["node", "server.js"]
+CMD ["node", "server.js"]

+ 2 - 2
README.md

@@ -1,7 +1,7 @@
 # DweebUI
 # DweebUI
 DweebUI is a web interface for managing Docker, with a zero-config dashboard for controlling and monitoring your containers.
 DweebUI is a web interface for managing Docker, with a zero-config dashboard for controlling and monitoring your containers.
 
 
-Alpha v0.20 ( :fire: Experimental :fire: )
+Alpha v0.21 ( :fire: Experimental :fire: )
 
 
    
    
 [:warning: DweebUI is a management interface and should not be directly exposed to the internet :warning:](https://github.com/lllllllillllllillll/DweebUI/wiki/Exposing-DweebUI-to-the-Internet)
 [:warning: DweebUI is a management interface and should not be directly exposed to the internet :warning:](https://github.com/lllllllillllllillll/DweebUI/wiki/Exposing-DweebUI-to-the-Internet)
@@ -10,7 +10,7 @@ Alpha v0.20 ( :fire: Experimental :fire: )
 [![GitHub Activity](https://img.shields.io/github/commit-activity/y/lllllllillllllillll/DweebUI)](https://github.com/lllllllillllllillll)
 [![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)
 [![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)
 [![GitHub License](https://img.shields.io/github/license/lllllllillllllillll/DweebUI)](https://github.com/lllllllillllllillll/DweebUI/blob/main/LICENSE)
-[![GitHub License](https://img.shields.io/badge/-buy_me_a%C2%A0coffee-gray?logo=buy-me-a-coffee)](https://www.buymeacoffee.com/lllllllillllllillll)
+[![Coffee](https://img.shields.io/badge/-buy_me_a%C2%A0coffee-gray?logo=buy-me-a-coffee)](https://www.buymeacoffee.com/lllllllillllllillll)
 
 
 * This is a personal project I started to get more familiar with Javascript and Node.js.
 * This is a personal project I started to get more familiar with Javascript and Node.js.
 * Some UI elements are placeholders and every version may have breaking changes.
 * Some UI elements are placeholders and every version may have breaking changes.

+ 10 - 8
components/containerCard.js

@@ -49,7 +49,7 @@ export const containerCard = (data) => {
 
 
 
 
   return `
   return `
-    <div class="col-sm-6 col-lg-3 deleteme">
+    <div class="col-sm-6 col-lg-3 pt-1">
       <div class="card">
       <div class="card">
         <div class="card-body">
         <div class="card-body">
           <div class="card-stamp card-stamp-sm">
           <div class="card-stamp card-stamp-sm">
@@ -60,16 +60,16 @@ export const containerCard = (data) => {
             <div class="ms-auto lh-1">
             <div class="ms-auto lh-1">
               <div class="card-actions btn-actions">
               <div class="card-actions btn-actions">
                 <div class="card-actions btn-actions">
                 <div class="card-actions btn-actions">
-                  <button onclick="clicked(this)" name="${name}" value="${state}" id="start" class="btn-action" title="Start" ${actions}><!-- player-play -->
+                  <button class="btn-action" title="Start" data-hx-get="/click" data-hx-trigger="click" data-hx-swap="none" name="${name}" id="start" value="${state}" ${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>
                     <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>
-                  <button onclick="clicked(this)" name="${name}" value="${state}" id="stop" class="btn-action" title="Stop" ${actions}><!-- player-stop -->
+                  <button class="btn-action" title="Stop" data-hx-get="/click" data-hx-trigger="click" data-hx-swap="none" name="${name}" id="stop" value="${state}" ${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>
                     <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>
-                  <button onclick="clicked(this)" name="${name}" value="${state}" id="pause" class="btn-action" title="Pause" ${actions}><!-- player-pause -->
+                  <button class="btn-action" title="Pause" data-hx-get="/click" data-hx-trigger="click" data-hx-swap="none" name="${name}" id="pause" value="${state}" ${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>
                     <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>
-                  <button onclick="clicked(this)" name="${name}" value="${state}" id="restart" class="btn-action" title="Restart" ${actions}><!-- reload -->
+                  <button class="btn-action" title="Restart" data-hx-get="/click" data-hx-trigger="click" data-hx-swap="none" name="${name}" id="restart" value="${state}" ${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>                          
                     <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>
                   </button>
                   <div class="dropdown">
                   <div class="dropdown">
@@ -89,8 +89,8 @@ export const containerCard = (data) => {
                       <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>
                       <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>
                     </a>
                     <div class="dropdown-menu dropdown-menu-end">
                     <div class="dropdown-menu dropdown-menu-end">
-                      <button class="dropdown-item text-secondary" onclick="clicked(this)" name="${name}" id="hide" value="hide">Hide</button>
-                      <button class="dropdown-item text-secondary" onclick="clicked(this)" name="${name}" id="resetView" value="resetView">Reset View</button>
+                      <button class="dropdown-item text-secondary" data-hx-get="/click" data-hx-trigger="click" data-hx-swap="none" name="${name}" id="hide" value="hide">Hide</button>
+                      <button class="dropdown-item text-secondary" data-hx-get="/click" data-hx-trigger="click" data-hx-swap="none" name="${name}" id="reset" value="reset">Reset View</button>
                       <button class="dropdown-item text-secondary" onclick="clicked(this)" name="${name}" id="permissions" value="permissions" data-bs-toggle="modal" data-bs-target="#${name}_permissions">Permissions</button>
                       <button class="dropdown-item text-secondary" onclick="clicked(this)" name="${name}" id="permissions" value="permissions" data-bs-toggle="modal" data-bs-target="#${name}_permissions">Permissions</button>
                     </div>
                     </div>
                   </div>
                   </div>
@@ -111,7 +111,9 @@ export const containerCard = (data) => {
               </span>
               </span>
             </div>
             </div>
           </div>
           </div>
-          <div id="${chart}_chart" class="chart-sm"></div>
+          <div class="chart-sm" data-hx-get="/chart" data-hx-trigger="every 2s" name="${name}" data-hx-target="#${name}_chart" hx-swap="innerHTML">
+            <div id="${name}_chart"></div>
+          </div>
         </div>
         </div>
       </div>
       </div>
     </div>
     </div>

+ 1 - 1
controllers/apps.js

@@ -1,7 +1,7 @@
 import { readFileSync } from 'fs';
 import { readFileSync } from 'fs';
 import { appCard } from '../components/appCard.js';
 import { appCard } from '../components/appCard.js';
 
 
-let templatesJSON = readFileSync('./templates.json');
+let templatesJSON = readFileSync('./templates/templates.json');
 let templates = JSON.parse(templatesJSON).templates;
 let templates = JSON.parse(templatesJSON).templates;
 
 
 templates = templates.sort((a, b) => {
 templates = templates.sort((a, b) => {

+ 2 - 0
controllers/login.js

@@ -1,6 +1,8 @@
 import { User, Syslog } from '../database/models.js';
 import { User, Syslog } from '../database/models.js';
 import bcrypt from 'bcrypt';
 import bcrypt from 'bcrypt';
 
 
+
+
 export const Login = function(req,res){
 export const Login = function(req,res){
     if(req.session.user){
     if(req.session.user){
         res.redirect("/logout");
         res.redirect("/logout");

+ 1 - 1
controllers/register.js

@@ -55,7 +55,7 @@ export const submitRegister = async function(req,res){
                     password: bcrypt.hashSync(password,10),
                     password: bcrypt.hashSync(password,10),
                     role: await userRole(),
                     role: await userRole(),
                     group: 'all',
                     group: 'all',
-                    avatar: `<img src="img/avatars/${avatar}">`,
+                    avatar: `<img src="/images/avatars/${avatar}">`,
                     lastLogin: newLogin,
                     lastLogin: newLogin,
                 });
                 });
 
 

+ 1 - 9
database/models.js

@@ -1,17 +1,9 @@
 import { Sequelize, DataTypes } from 'sequelize';
 import { Sequelize, DataTypes } from 'sequelize';
 
 
-// let SQLITE_PASS = process.env.SQLITE_PASS || 'some_long_elaborate_password';
-
-// export const sequelize = new Sequelize('dweebui', 'dweebui', SQLITE_PASS, { 
-//   dialect: 'sqlite',
-//   dialectModulePath: '@journeyapps/sqlcipher',
-//   storage: './database/database.sqlite',
-//   logging: false,
-// });
 
 
 export const sequelize = new Sequelize({ 
 export const sequelize = new Sequelize({ 
   dialect: 'sqlite',
   dialect: 'sqlite',
-  storage: './database/database.sqlite',
+  storage: './database/db.sqlite',
   logging: false,
   logging: false,
 });
 });
 
 

+ 2 - 3
docker-compose.yaml

@@ -2,16 +2,15 @@ version: "3.9"
 services:
 services:
   dweebui:
   dweebui:
     container_name: dweebui
     container_name: dweebui
-    image: lllllllillllllillll/dweebui:v0.20
+    image: lllllllillllllillll/dweebui:v0.21-dev
     environment:
     environment:
-      NODE_ENV: production
       PORT: 8000
       PORT: 8000
       SECRET: MrWiskers
       SECRET: MrWiskers
     restart: unless-stopped
     restart: unless-stopped
     ports:
     ports:
       - 8000:8000
       - 8000:8000
     volumes:
     volumes:
-      - dweebui:/app
+      - dweebui:/app/database/db.sqlite
       - /var/run/docker.sock:/var/run/docker.sock
       - /var/run/docker.sock:/var/run/docker.sock
     networks:
     networks:
       - dweebui_net
       - dweebui_net

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 32 - 854
package-lock.json


+ 5 - 15
package.json

@@ -1,37 +1,27 @@
 {
 {
   "name": "dweebui",
   "name": "dweebui",
   "version": "1.0.0",
   "version": "1.0.0",
-  "description": "A web UI for Docker",
+  "description": "",
   "main": "server.js",
   "main": "server.js",
   "type": "module",
   "type": "module",
   "scripts": {
   "scripts": {
-    "test": "mocha --require @babel/register"
+    "test": "echo \"Error: no test specified\" && exit 1",
+    "start": "node server.js"
   },
   },
   "keywords": [],
   "keywords": [],
   "author": "",
   "author": "",
   "license": "ISC",
   "license": "ISC",
   "dependencies": {
   "dependencies": {
-    "@babel/register": "^7.23.7",
-    "@socket.io/admin-ui": "^0.5.1",
     "bcrypt": "^5.1.1",
     "bcrypt": "^5.1.1",
-    "chai": "^5.0.0",
-    "compression": "^1.7.4",
-    "cors": "^2.8.5",
     "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.2",
     "express": "^4.18.2",
-    "express-rate-limit": "^7.1.5",
     "express-session": "^1.17.3",
     "express-session": "^1.17.3",
-    "helmet": "^7.1.0",
     "js-yaml": "^4.1.0",
     "js-yaml": "^4.1.0",
-    "mocha": "^10.2.0",
+    "memorystore": "^1.6.7",
     "sequelize": "^6.35.2",
     "sequelize": "^6.35.2",
-    "sinon": "^17.0.1",
-    "socket.io": "^4.7.4",
     "sqlite3": "^5.1.7",
     "sqlite3": "^5.1.7",
-    "stream": "^0.0.2",
-    "supertest": "^6.3.3",
-    "systeminformation": "^5.21.22"
+    "systeminformation": "^5.21.23"
   }
   }
 }
 }

+ 0 - 0
public/img/avatars/burns.jpg → public/images/avatars/burns.jpg


+ 0 - 0
public/img/avatars/duffman.png → public/images/avatars/duffman.png


+ 0 - 0
public/img/avatars/frank.jpg → public/images/avatars/frank.jpg


+ 0 - 0
public/img/avatars/moe.jpg → public/images/avatars/moe.jpg


+ 0 - 0
public/img/avatars/moleman.png → public/images/avatars/moleman.png


+ 0 - 0
public/img/avatars/poochie.jpg → public/images/avatars/poochie.jpg


+ 0 - 0
public/img/avatars/rus.jpg → public/images/avatars/rus.jpg


+ 0 - 0
public/img/avatars/skinner.jpg → public/images/avatars/skinner.jpg


+ 0 - 0
public/static/logo.svg → public/images/logo.svg


+ 0 - 141
public/js/main.js

@@ -1,141 +0,0 @@
-socket.on('connect', () => { 
-  console.log('connected'); 
-  //clear localStorage (because of code in old versions)
-  localStorage.clear();
-});
-
-// Server metrics
-const cpuText = document.getElementById('cpu-text');
-const cpuBar = document.getElementById('cpu-bar');
-const ramText = document.getElementById('ram-text');
-const ramBar = document.getElementById('ram-bar');
-const netText = document.getElementById('net-text');
-const netBar = document.getElementById('net-bar');
-const diskText = document.getElementById('disk-text');
-const diskBar = document.getElementById('disk-bar');
-
-// Container cards
-const dockerCards = document.getElementById('cards');
-
-// Container logs
-const logViewer = document.getElementById('logView');
-
-// Server metrics
-socket.on('metrics', (data) => {
-    let [cpu, ram, tx, rx, disk] = data;
-
-    cpuText.innerHTML = `<span>CPU ${cpu} %</span>`;
-    if (cpu < 7 ) { cpu = 7; }
-    cpuBar.innerHTML = `<span style="width: ${cpu}%"><span></span></span>`;
-    
-    ramText.innerHTML = `<span>RAM ${ram} %</span>`;
-    if (ram < 7 ) { ram = 7; }
-    ramBar.innerHTML = `<span style="width: ${ram}%"><span></span></span>`;
-
-    tx = Math.round(tx / 1024 / 1024);
-    rx = Math.round(rx / 1024 / 1024);
-
-    netText.innerHTML = `<span>Down: ${rx}MB</span><span>  Up: ${tx}MB</span>`;
-    netBar.innerHTML = `<span style="width: 50%"><span></span></span>`;
-
-    diskText.innerHTML = `<span>DISK ${disk} %</span>`;
-    if (disk < 7 ) { disk = 7; }
-    diskBar.innerHTML = `<span style="width: ${disk}%"><span></span></span>`;
-});
-
-// Container cards
-socket.on('containers', (data) => {
-    let deleteMeElements = document.querySelectorAll('.deleteme');
-    deleteMeElements.forEach((element) => {
-      element.parentNode.removeChild(element);
-    });
-    dockerCards.insertAdjacentHTML("afterend", data);
-});
-
-
-function drawCharts(name, cpuArray, ramArray) {
-  let element = document.querySelector(`${name}`);
-
-  let chart = new ApexCharts(element, {
-    chart: {
-      type: "line",
-      height: 40.0,
-      sparkline: {
-        enabled: true
-      },
-      animations: {
-        enabled: false
-      }
-    },
-    fill: {
-      opacity: 1
-    },
-    stroke: {
-      width: [2, 1],
-      dashArray: [0, 3],
-      lineCap: "round",
-      curve: "smooth"
-    },
-    series: [{
-      name: "CPU",
-      data: cpuArray
-    }, {
-      name: "RAM",
-      data: ramArray
-    }],
-    tooltip: {
-      enabled: false
-    },
-    grid: {
-      strokeDashArray: 4
-    },
-    xaxis: {
-      labels: {
-        padding: 0
-      },
-      tooltip: {
-        enabled: false
-      }
-    },
-    yaxis: {
-      labels: {
-        padding: 4
-      }
-    },
-    colors: [tabler.getColor("primary"), tabler.getColor("gray-600")],
-    legend: {
-      show: false
-    }
-  })
-  chart.render();
-}
-
-// Buttons functions
-function clicked(button) {
-  socket.emit('clicked', {name: button.name, id: button.id, value: button.value});
-}
-
-
-socket.on('containerStats', (data) => {
-  let containerStats = data;
-  
-  for (const [name, statsArray] of Object.entries(containerStats)) {
-
-    let cpuArray = statsArray.cpuArray;
-    let ramArray = statsArray.ramArray;
-
-    let chart = document.getElementById(`${name}_chart`);
-    if (chart) {
-      chart.innerHTML = '';
-      drawCharts(`#${name}_chart`, cpuArray, ramArray);
-    } else {
-      console.log(`Chart element with id ${name}_chart not found in the DOM`);
-    }
-  }
-
-});
-
-
-socket.on('logs', (data) => {
-  logViewer.innerHTML = `<pre>${data}</pre>`;
-});

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
public/static/logo-sm-black.svg


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
public/static/logo-sm-white.svg


+ 3 - 2
router/index.js

@@ -15,10 +15,11 @@ import { Volumes } from "../controllers/volumes.js";
 import { Syslogs } from "../controllers/syslogs.js";
 import { Syslogs } from "../controllers/syslogs.js";
 import { Portal } from "../controllers/portal.js"
 import { Portal } from "../controllers/portal.js"
 
 
-/// Functions
+// Functions
 import { Install } from "../functions/install.js"
 import { Install } from "../functions/install.js"
 import { Uninstall } from "../functions/uninstall.js"
 import { Uninstall } from "../functions/uninstall.js"
 
 
+
 // Auth middleware
 // Auth middleware
 const auth = (req, res, next) => {
 const auth = (req, res, next) => {
     if (req.session.role == "admin") {
     if (req.session.role == "admin") {
@@ -58,4 +59,4 @@ router.get("/settings", auth, Settings);
 
 
 // Functions
 // Functions
 router.post("/install", auth, Install);
 router.post("/install", auth, Install);
-router.post("/uninstall", auth, Uninstall);
+router.post("/uninstall", auth, Uninstall);

+ 205 - 224
server.js

@@ -1,44 +1,26 @@
 import express from 'express';
 import express from 'express';
 import session from 'express-session';
 import session from 'express-session';
-import compression from 'compression';
-import helmet from 'helmet';
+import memorystore from 'memorystore';
+import ejs from 'ejs';
 import Docker from 'dockerode';
 import Docker from 'dockerode';
-import cors from 'cors';
 import { Readable } from 'stream';
 import { Readable } from 'stream';
-import { rateLimit } from 'express-rate-limit';
-import { instrument } from '@socket.io/admin-ui'
 import { router } from './router/index.js';
 import { router } from './router/index.js';
-import { createServer } from 'node:http';
-import { Server } from 'socket.io';
 import { sequelize, Container } from './database/models.js';
 import { sequelize, Container } from './database/models.js';
 import { currentLoad, mem, networkStats, fsSize, dockerContainerStats, dockerImages, networkInterfaces } from 'systeminformation';
 import { currentLoad, mem, networkStats, fsSize, dockerContainerStats, dockerImages, networkInterfaces } from 'systeminformation';
 import { containerCard } from './components/containerCard.js';
 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();
 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, { 
-    cors: {
-        origin: ['http://localhost:8000', 'https://admin.socket.io'],
-        methods: ['GET', 'POST'],
-        credentials: true
-    } 
-});
-instrument(io, {
-    auth: false,
-    readonly: true
-});
+const app = express();
+const MemoryStore = memorystore(session);
+const port = process.env.PORT || 8000;
+let [ hidden, activeEvent, cardList, clicked ] = ['', '', '', false];
+let sentList = '';
+let SSE = false;
+let clicks = 0;
 
 
 // Session middleware
 // Session middleware
 const sessionMiddleware = session({
 const sessionMiddleware = session({
+    store: new MemoryStore({ checkPeriod: 86400000 }), // Prune expired entries every 24h
     secret: "keyboard cat", 
     secret: "keyboard cat", 
     resave: false, 
     resave: false, 
     saveUninitialized: false, 
     saveUninitialized: false, 
@@ -49,47 +31,40 @@ const sessionMiddleware = session({
     }
     }
 });
 });
 
 
-// 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
 // Express middleware
-app.set('view engine', 'ejs');
+app.set('view engine', 'html');
+app.engine('html', ejs.renderFile);
 app.use([
 app.use([
-    compression(),
-    cors(),
-    helmet({contentSecurityPolicy: false}),
-    express.static("public"),
+    express.static('public'),
     express.json(),
     express.json(),
     express.urlencoded({ extended: true }),
     express.urlencoded({ extended: true }),
     sessionMiddleware,
     sessionMiddleware,
-    router,
-    limiter
+    router
 ]);
 ]);
 
 
 // Initialize server
 // Initialize server
-server.listen(port, async () => {
+app.listen(port, async () => {
     async function init() {
     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();
+        try { await sequelize.authenticate().then(
+            () => { console.log('DB Connection: ✔️') }); } 
+            catch { console.log('DB Connection: ❌'); }
+        try { await sequelize.sync().then( // check out that formatting
+            () => { console.log('Synced Models: ✔️') }); } 
+            catch { console.log('Synced Models: ❌'); }
     }
     }
-    await init();
-    app.emit("appStarted");
-    console.log(`\nServer listening on http://localhost:${port}`);
+    await init().then(() => { 
+        console.log(`Listening on http://localhost:${port} ✔️`);
+    });
 });
 });
 
 
+// Get hidden containers
+async function getHidden() {
+    hidden = await Container.findAll({ where: {visibility:false}});
+    hidden = hidden.map((container) => container.name);
+}
+
 // Server metrics
 // Server metrics
+let [ cpu, ram, tx, rx, disk ] = [0, 0, 0, 0, 0];
 let serverMetrics = async () => {
 let serverMetrics = async () => {
     currentLoad().then(data => { 
     currentLoad().then(data => { 
         cpu = Math.round(data.currentLoad); 
         cpu = Math.round(data.currentLoad); 
@@ -105,8 +80,9 @@ let serverMetrics = async () => {
         disk = data[0].use; 
         disk = data[0].use; 
     });
     });
 }
 }
+setInterval(serverMetrics, 1000);
 
 
-// List docker containers
+// Docker containers
 let containerCards = async () => {
 let containerCards = async () => {
     let list = '';
     let list = '';
     const allContainers = await docker.listContainers({ all: true });
     const allContainers = await docker.listContainers({ all: true });
@@ -151,193 +127,198 @@ let containerCards = async () => {
     cardList = list;
     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 
 // Store docker events 
 docker.getEvents((err, stream) => {
 docker.getEvents((err, stream) => {
     if (err) throw err;
     if (err) throw err;
     stream.on('data', (chunk) => {
     stream.on('data', (chunk) => {
-        dockerEvents += chunk.toString('utf8');
+        activeEvent += chunk.toString('utf8');
     });
     });
 });
 });
 
 
-// Check for docker events
+// Check if the container cards need to be updated
 setInterval(async () => {
 setInterval(async () => {
-    if (dockerEvents != '') {
-        await getHidden();
-        await containerCards();
-        dockerEvents = '';
+    if (activeEvent == '') { return; }
+    activeEvent = '';
+    await getHidden();
+    await containerCards();
+    if (cardList != sentList) {
+        cardList = sentList;
+        SSE = true;
     }
     }
 }, 1000);
 }, 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) => {
-            let { name, id, value } = data;
-            console.log(`${sessionData.user} clicked: ${id} ${value} ${name}`);
-            if (clicked == true) { return; } clicked = true;
-
-            // 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);
-                });
-            }
-
-            // 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');
+// HTMX triggers
+router.get('/stats', async (req, res) => {
+    switch (req.header('HX-Trigger')) {
+        case 'cpu': 
+        let info = '<div class="font-weight-medium">';
+            info += '<label class="cpu-text mb-1" for="cpu">CPU ' + cpu + '%</label>';
+            info += '</div>';
+            info += '<div class="cpu-bar meter animate">';
+            info += '<span style="width:' + cpu + '%"><span></span></span>';
+            info += '</div>';
+            res.send(info);
+            break;
+        case 'ram':
+        let info2 = '<div class="font-weight-medium">';
+            info2 += '<label class="ram-text mb-1" for="ram">RAM ' + ram + '%</label>';
+            info2 += '</div>';
+            info2 += '<div class="ram-bar meter animate orange">';
+            info2 += '<span style="width:' + ram + '%"><span></span></span>';
+            info2 += '</div>';
+            res.send(info2);
+            break;
+        case 'tx':
+            res.send('TX ' + tx.toFixed(2) + ' MB');
+            break;
+        case 'rx':
+            res.send('RX ' + rx.toFixed(2) + ' MB');
+            break;
+        case 'disk':
+        let info5 = '<div class="font-weight-medium">';
+            info5 += '<label class="disk-text mb-1" for="disk">Disk ' + disk + '%</label>';
+            info5 += '</div>';
+            info5 += '<div class="disk-bar meter animate red">';
+            info5 += '<span style="width:' + disk + '%"><span></span></span>';
+            info5 += '</div>';
+            res.send(info5);
+            break;
+        default:
+            console.log('Unknown trigger');
+            break;
     }
     }
 });
 });
 
 
+router.get('/containers', async (req, res) => {
+    await getHidden();
+    await containerCards();
+    sentList = cardList;
+    res.send(cardList);
+});
 
 
+// Server-side event trigger
+router.get('/sse_event', (req, res) => {
+    res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', });
 
 
+    let eventCheck = setInterval(async () => {
+        if (SSE == true) {
+            SSE = false;
+            res.write(`event: docker\n`);
+            res.write(`data: there was a docker event!\n\n`);
+            console.log(`server-side event sent`)
+        }
+    }, 1000);
 
 
+    req.on('close', () => {
+        clearInterval(eventCheck);
+    });
+});
 
 
+// HTMX buttons
+router.get('/click', async (req, res) => {
+    
+    let name = req.header('hx-trigger-name');
+    let id = req.header('hx-trigger');
+    let value = req.query[name];
+
+    // 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') {
+        let exists = await Container.findOne({ where: {name: name}});
+        if (!exists) {
+            const newContainer = await Container.create({ name: name, visibility: false, });
+            SSE = true;
+        } else {
+            exists.update({ visibility: false });
+            SSE = true;
+        }
+    }
 
 
+    // reset hidden
+    if (id == 'reset') {
+        Container.update({ visibility: true }, { where: {} });
+        SSE = true;
+    }
+});
 
 
-// let link = '';
-// networkInterfaces().then(data => {
-//     link = data[0].ip4;
-// });
+// container charts
+router.get('/chart', async (req, res) => {
+    let name = req.header('hx-trigger-name');
+    let chart = `
+    <script>
+    var options = {
+        chart: {
+          type: "line",
+          height: 40.0,
+          sparkline: {
+            enabled: true
+          },
+          animations: {
+            enabled: false
+          }
+        },
+        fill: {
+          opacity: 1
+        },
+        stroke: {
+          width: [2, 1],
+          dashArray: [0, 3],
+          lineCap: "round",
+          curve: "smooth"
+        },
+        series: [{
+          name: "CPU",
+          data: [0,10,0,10,0,10,0,10,0,10]
+        }, {
+          name: "RAM",
+          data: [0,5,0,5,0,5,0,5,0,5]
+        }],
+        tooltip: {
+          enabled: false
+        },
+        grid: {
+          strokeDashArray: 4
+        },
+        xaxis: {
+          labels: {
+            padding: 0
+          },
+          tooltip: {
+            enabled: false
+          }
+        },
+        yaxis: {
+          labels: {
+            padding: 4
+          }
+        },
+        colors: [tabler.getColor("primary"), tabler.getColor("gray-600")],
+        legend: {
+          show: false
+        }
+    }
 
 
+    var chart = new ApexCharts(document.querySelector("#${name}_chart"), options);
+    chart.render();
+    </script>`
+    res.send(chart);
+});

+ 0 - 0
templates.json → templates/templates.json


+ 2 - 2
views/account.ejs → views/account.html

@@ -21,7 +21,7 @@
 	<body >
 	<body >
 		<div class="page">
 		<div class="page">
 		<!-- Navbar -->
 		<!-- Navbar -->
-		<%- include('navbar.ejs') %>
+		<%- include('navbar.html') %>
 		<div class="page-wrapper">
 		<div class="page-wrapper">
 			<!-- Page header -->
 			<!-- Page header -->
 			<div class="page-header d-print-none">
 			<div class="page-header d-print-none">
@@ -133,7 +133,7 @@
 				</div>
 				</div>
 			</div>
 			</div>
 			</div>
 			</div>
-			<%- include('footer.ejs') %>
+			<%- include('footer.html') %>
 		</div>
 		</div>
 		</div>
 		</div>
 		<!-- Libs JS -->
 		<!-- Libs JS -->

+ 2 - 2
views/apps.ejs → views/apps.html

@@ -22,7 +22,7 @@
     <div class="page">
     <div class="page">
       <!-- Navbar -->
       <!-- Navbar -->
 
 
-      <%- include('navbar.ejs') %>
+      <%- include('navbar.html') %>
 
 
       <div class="page-wrapper">
       <div class="page-wrapper">
         <!-- Page header -->
         <!-- Page header -->
@@ -98,7 +98,7 @@
           </div>
           </div>
         </div>
         </div>
         
         
-        <%- include('footer.ejs') %>
+        <%- include('footer.html') %>
 
 
       </div>
       </div>
     </div>
     </div>

+ 37 - 28
views/dashboard.ejs → views/dashboard.html

@@ -8,6 +8,8 @@
     <!-- CSS files -->
     <!-- CSS files -->
     <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="https://unpkg.com/htmx.org@1.9.10" integrity="sha384-D1Kt99CQMDuVetoL1lrYwg5t+9QdHe7NLX/SoJYkXDFfX37iInKRy5xLSi8nO7UC" crossorigin="anonymous"></script>
+    <script src="https://unpkg.com/htmx.org/dist/ext/sse.js"></script>
     <style>
     <style>
       @import url('/fonts/inter.css');
       @import url('/fonts/inter.css');
       :root {
       :root {
@@ -19,16 +21,18 @@
     </style>
     </style>
   </head>
   </head>
   <body >
   <body >
+  
   <div class="page">
   <div class="page">
 
 
-    <%- include('navbar.ejs') %>
+    <%- include('navbar.html') %>
+    
     <div class="page-wrapper">
     <div class="page-wrapper">
 
 
       <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">
             
             
-            <div class="col-12" id="cards">
+            <div class="col-12">
               <div class="row row-cards">
               <div class="row row-cards">
                 
                 
                 <div class="col-sm-6 col-lg-3">
                 <div class="col-sm-6 col-lg-3">
@@ -40,14 +44,17 @@
                             <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-cpu" 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 5m0 1a1 1 0 0 1 1 -1h12a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-12a1 1 0 0 1 -1 -1z"></path><path d="M9 9h6v6h-6z"></path><path d="M3 10h2"></path><path d="M3 14h2"></path><path d="M10 3v2"></path><path d="M14 3v2"></path><path d="M21 10h-2"></path><path d="M21 14h-2"></path><path d="M14 21v-2"></path><path d="M10 21v-2"></path></svg>
                             <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-cpu" 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 5m0 1a1 1 0 0 1 1 -1h12a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-12a1 1 0 0 1 -1 -1z"></path><path d="M9 9h6v6h-6z"></path><path d="M3 10h2"></path><path d="M3 14h2"></path><path d="M10 3v2"></path><path d="M14 3v2"></path><path d="M21 10h-2"></path><path d="M21 14h-2"></path><path d="M14 21v-2"></path><path d="M10 21v-2"></path></svg>
                           </span>
                           </span>
                         </div>
                         </div>
-                        <div class="col">
+
+                        <!-- HTMX -->
+                        <div class="col" id="cpu" data-hx-get="/stats" data-hx-trigger="load, every 1s" data-hx-target="#cpu">
                           <div class="font-weight-medium">
                           <div class="font-weight-medium">
-                            <label id="cpu-text" class="cpu-text mb-1" for="cpu">CPU 0%</label>
+                            <label class="cpu-text mb-1" for="cpu">CPU 0%</label>
                           </div>
                           </div>
-                          <div id="cpu-bar" class="cpu-bar meter animate">
-                            <span style="width: 25%"><span></span></span>
+                          <div class="cpu-bar meter animate">
+                            <span style="width:20%"><span></span></span>
                           </div>
                           </div>
                         </div>
                         </div>
+
                       </div>
                       </div>
                     </div>
                     </div>
                   </div>
                   </div>
@@ -62,14 +69,17 @@
                             <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-container" 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="M20 4v.01"></path> <path d="M20 20v.01"></path> <path d="M20 16v.01"></path> <path d="M20 12v.01"></path> <path d="M20 8v.01"></path> <path d="M8 4m0 1a1 1 0 0 1 1 -1h6a1 1 0 0 1 1 1v14a1 1 0 0 1 -1 1h-6a1 1 0 0 1 -1 -1z"></path> <path d="M4 4v.01"></path> <path d="M4 20v.01"></path> <path d="M4 16v.01"></path> <path d="M4 12v.01"></path> <path d="M4 8v.01"></path> </svg>                            
                             <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-container" 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="M20 4v.01"></path> <path d="M20 20v.01"></path> <path d="M20 16v.01"></path> <path d="M20 12v.01"></path> <path d="M20 8v.01"></path> <path d="M8 4m0 1a1 1 0 0 1 1 -1h6a1 1 0 0 1 1 1v14a1 1 0 0 1 -1 1h-6a1 1 0 0 1 -1 -1z"></path> <path d="M4 4v.01"></path> <path d="M4 20v.01"></path> <path d="M4 16v.01"></path> <path d="M4 12v.01"></path> <path d="M4 8v.01"></path> </svg>                            
                           </span>
                           </span>
                         </div>
                         </div>
-                        <div class="col">
+
+                        <!-- HTMX -->
+                        <div class="col" id="ram" data-hx-get="/stats" data-hx-trigger="load, every 2s">
                           <div class="font-weight-medium">
                           <div class="font-weight-medium">
-                            <label id="ram-text" class="ram-text mb-1" for="ram">RAM 0%</label>
+                            <label class="ram-text mb-1" for="ram">RAM 0%</label>
                           </div>
                           </div>
-                          <div id="ram-bar" class="ram-bar meter animate orange">
-                            <span style="width: 25%"><span></span></span>
+                          <div class="ram-bar meter animate orange">
+                            <span style="width:20%"><span></span></span>
                           </div>
                           </div>
                         </div>
                         </div>
+
                       </div>
                       </div>
                     </div>
                     </div>
                   </div>
                   </div>
@@ -89,7 +99,7 @@
                             <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>
                           <div id="net-bar" class="meter animate blue">
                           <div id="net-bar" class="meter animate blue">
-                            <span style="width: 25%"><span></span></span>
+                            <span style="width: 20%"><span></span></span>
                           </div>
                           </div>
                         </div>
                         </div>
                       </div>
                       </div>
@@ -106,14 +116,17 @@
                             <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-database" 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 6m-8 0a8 3 0 1 0 16 0a8 3 0 1 0 -16 0"></path> <path d="M4 6v6a8 3 0 0 0 16 0v-6"></path> <path d="M4 12v6a8 3 0 0 0 16 0v-6"></path></svg>
                             <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-database" 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 6m-8 0a8 3 0 1 0 16 0a8 3 0 1 0 -16 0"></path> <path d="M4 6v6a8 3 0 0 0 16 0v-6"></path> <path d="M4 12v6a8 3 0 0 0 16 0v-6"></path></svg>
                           </span>
                           </span>
                         </div>
                         </div>
-                        <div class="col">
+
+                        <!-- HTMX -->
+                        <div class="col" id="disk" data-hx-get="/stats" data-hx-trigger="load, every 2s">
                           <div class="font-weight-medium">
                           <div class="font-weight-medium">
-                            <label id="disk-text" class="disk-text mb-1" for="disk">DISK 0%</label>
+                            <label class="disk-text mb-1" for="disk">DISK 0%</label>
                           </div>
                           </div>
-                          <div id="disk-bar" class="meter animate red">
-                            <span style="width: 25%"><span></span></span>
+                          <div class="meter animate red">
+                            <span style="width:20%"><span></span></span>
                           </div>
                           </div>
                         </div>
                         </div>
+
                       </div>
                       </div>
                     </div>
                     </div>
                   </div>
                   </div>
@@ -123,9 +136,13 @@
               </div>
               </div>
             </div>
             </div>
 
 
+            <!-- HTMX -->
+            <div class="col-12" hx-ext="sse" sse-connect="/sse_event">
+              <div class="row row-cards" data-hx-get="/containers" data-hx-trigger="load, sse:docker" data-hx-swap="innerHTML">
+
+              </div>
+            </div>
 
 
-            <!-- containerCards get inserted here from public/js/main.js -->
-            
             <div class="modal modal-blur fade" id="details_modal" tabindex="-1" role="dialog" aria-hidden="true">
             <div class="modal modal-blur fade" id="details_modal" tabindex="-1" role="dialog" aria-hidden="true">
                         <div class="modal-dialog modal-dialog-centered modal-dialog-scrollable" role="document">
                         <div class="modal-dialog modal-dialog-centered modal-dialog-scrollable" role="document">
                           <div class="modal-content">
                           <div class="modal-content">
@@ -989,23 +1006,15 @@
         </div>
         </div>
       </div>
       </div>
       
       
-      <%- include('footer.ejs') %>
+    <%- include('footer.html') %>
       
       
     </div>
     </div>
   </div>
   </div>
     
     
 
 
-  <!-- Libs JS -->
-  <script src="/libs/apexcharts/dist/apexcharts.min.js" defer></script>
-  <!-- Tabler Core -->
+  <script src="/libs/apexcharts/dist/apexcharts.min.js"></script>
   <script src="/js/tabler.min.js"></script>
   <script src="/js/tabler.min.js"></script>
-  <!-- Socket.io -->
-  <script src="/socket.io/socket.io.js"></script>
-  <script>
-    const socket = io();
-  </script>
-  <script src="/js/main.js"></script>
-    
+
   </body>
   </body>
 </html>
 </html>
 
 

+ 1 - 1
views/footer.ejs → views/footer.html

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

+ 2 - 2
views/images.ejs → views/images.html

@@ -20,7 +20,7 @@
   <body >
   <body >
     <div class="page">
     <div class="page">
       <!-- Navbar -->
       <!-- Navbar -->
-      <%- include('navbar.ejs') %>
+      <%- include('navbar.html') %>
       <div class="page-wrapper">
       <div class="page-wrapper">
         <!-- Page header -->
         <!-- Page header -->
         
         
@@ -144,7 +144,7 @@
           </div>
           </div>
         </div>
         </div>
         
         
-        <%- include('footer.ejs') %>
+        <%- include('footer.html') %>
         
         
       </div>
       </div>
     </div>
     </div>

+ 17 - 7
views/login.ejs → views/login.html

@@ -9,7 +9,7 @@
     <link href="/css/tabler.min.css" rel="stylesheet"/>
     <link href="/css/tabler.min.css" rel="stylesheet"/>
     <link href="/css/demo.min.css" rel="stylesheet"/>
     <link href="/css/demo.min.css" rel="stylesheet"/>
     <style>
     <style>
-      @import url('fonts/inter.css');
+      @import url('/fonts/inter.css');
       :root {
       :root {
         --tblr-font-sans-serif: 'Inter Var', -apple-system, BlinkMacSystemFont, San Francisco, Segoe UI, Roboto, Helvetica Neue, sans-serif;
         --tblr-font-sans-serif: 'Inter Var', -apple-system, BlinkMacSystemFont, San Francisco, Segoe UI, Roboto, Helvetica Neue, sans-serif;
       }
       }
@@ -17,24 +17,29 @@
         font-feature-settings: "cv03", "cv04", "cv11";
         font-feature-settings: "cv03", "cv04", "cv11";
       }
       }
     </style>
     </style>
+    <script src="https://unpkg.com/htmx.org@1.9.10" integrity="sha384-D1Kt99CQMDuVetoL1lrYwg5t+9QdHe7NLX/SoJYkXDFfX37iInKRy5xLSi8nO7UC" crossorigin="anonymous"></script>
   </head>
   </head>
   <body  class=" d-flex flex-column">
   <body  class=" d-flex flex-column">
     <script src="/js/demo-theme.js"></script>
     <script src="/js/demo-theme.js"></script>
     <div class="page page-center">
     <div class="page page-center">
       <div class="container container-tight py-4">
       <div class="container container-tight py-4">
         <div class="text-center mb-4">
         <div class="text-center mb-4">
-          <a href="." class="navbar-brand navbar-brand-autodark"><img src="/static/logo.svg" height="50" alt=""></a>
+          <h1 class="navbar-brand navbar-brand-autodark d-none-navbar-horizontal pe-0 pe-md-3">
+            
+              <img src="/images/logo.svg" alt="DweebUI" title="DweebUI" class="navbar-brand-image">
+            
+          </h1>
         </div>
         </div>
         <div class="card card-md">
         <div class="card card-md">
           <div class="card-body">
           <div class="card-body">
             <h2 class="h2 text-center mb-4">Login to your account</h2>
             <h2 class="h2 text-center mb-4">Login to your account</h2>
             <form action="/login" method="POST" novalidate>
             <form action="/login" method="POST" novalidate>
 
 
-                <% if(error) { %>
-                    <div class="alert alert-danger" role="alert">
-                        <%= error %>
-                    </div>
-                <% } %>
+              <% if(error) { %>
+                <div class="alert alert-danger" role="alert">
+                    <%= error %>
+                </div>
+              <% } %>
 
 
               <div class="mb-2">
               <div class="mb-2">
                 <label class="form-label">Email address</label>
                 <label class="form-label">Email address</label>
@@ -70,9 +75,14 @@
         </div>
         </div>
       </div>
       </div>
     </div>
     </div>
+
+    <button class="btn btn-success" hx-get="/click" hx-trigger="click" hx-swap="innerHTML" id="incriment">0</button>
+ 
     <!-- Libs JS -->
     <!-- Libs JS -->
     <!-- Tabler Core -->
     <!-- Tabler Core -->
     <script src="/js/tabler.min.js" defer></script>
     <script src="/js/tabler.min.js" defer></script>
     <script src="/js/demo.min.js" defer></script>
     <script src="/js/demo.min.js" defer></script>
+    
+
   </body>
   </body>
 </html>
 </html>

+ 1 - 1
views/navbar.ejs → views/navbar.html

@@ -35,7 +35,7 @@
     </button>
     </button>
     <h1 class="navbar-brand navbar-brand-autodark d-none-navbar-horizontal pe-0 pe-md-3">
     <h1 class="navbar-brand navbar-brand-autodark d-none-navbar-horizontal pe-0 pe-md-3">
       <a href="#">
       <a href="#">
-        <img src="/static/logo.svg" alt="DweebUI" title="DweebUI" class="navbar-brand-image">
+        <img src="/images/logo.svg" alt="DweebUI" title="DweebUI" class="navbar-brand-image">
       </a>
       </a>
     </h1>
     </h1>
     <div class="navbar-nav flex-row order-md-last">
     <div class="navbar-nav flex-row order-md-last">

+ 2 - 2
views/networks.ejs → views/networks.html

@@ -20,7 +20,7 @@
   <body >
   <body >
     <div class="page">
     <div class="page">
       <!-- Navbar -->
       <!-- Navbar -->
-      <%- include('navbar.ejs') %>
+      <%- include('navbar.html') %>
       <div class="page-wrapper">
       <div class="page-wrapper">
         <!-- Page header -->
         <!-- Page header -->
         
         
@@ -144,7 +144,7 @@
           </div>
           </div>
         </div>
         </div>
         
         
-        <%- include('footer.ejs') %>
+        <%- include('footer.html') %>
         
         
       </div>
       </div>
     </div>
     </div>

+ 2 - 2
views/portal.ejs → views/portal.html

@@ -21,7 +21,7 @@
   <body >
   <body >
     <div class="page">
     <div class="page">
 
 
-      <%- include('navbar.ejs') %>
+      <%- include('navbar.html') %>
       <div class="page-wrapper">
       <div class="page-wrapper">
   
   
         <div class="page-body">
         <div class="page-body">
@@ -131,7 +131,7 @@
           </div>
           </div>
         </div>
         </div>
         
         
-        <%- include('footer.ejs') %>
+        <%- include('footer.html') %>
         
         
       </div>
       </div>
     </div>
     </div>

+ 10 - 12
views/register.ejs → views/register.html

@@ -9,7 +9,7 @@
     <link href="/css/tabler.min.css" rel="stylesheet"/>
     <link href="/css/tabler.min.css" rel="stylesheet"/>
     <link href="/css/demo.min.css" rel="stylesheet"/>
     <link href="/css/demo.min.css" rel="stylesheet"/>
     <style>
     <style>
-      @import url('fonts/inter.css');
+      @import url('/fonts/inter.css');
       :root {
       :root {
         --tblr-font-sans-serif: 'Inter Var', -apple-system, BlinkMacSystemFont, San Francisco, Segoe UI, Roboto, Helvetica Neue, sans-serif;
         --tblr-font-sans-serif: 'Inter Var', -apple-system, BlinkMacSystemFont, San Francisco, Segoe UI, Roboto, Helvetica Neue, sans-serif;
       }
       }
@@ -26,7 +26,7 @@
       <form class="container container-tight py-4" action="/register" method="POST" novalidate>
       <form class="container container-tight py-4" action="/register" method="POST" novalidate>
         
         
         <div class="text-center mb-4">
         <div class="text-center mb-4">
-          <a href="#" class="navbar-brand navbar-brand-autodark d-none"><img src="/static/logo.svg" height="36" alt=""></a>
+          <a href="#" class="navbar-brand navbar-brand-autodark d-none"><img src="/images/logo.svg" height="36" alt=""></a>
         </div>
         </div>
 
 
         <div class="card">
         <div class="card">
@@ -88,7 +88,7 @@
                   <label class="form-imagecheck mb-2">
                   <label class="form-imagecheck mb-2">
                     <input name="avatar" type="radio" value="rus.jpg" class="form-imagecheck-input" checked/>
                     <input name="avatar" type="radio" value="rus.jpg" class="form-imagecheck-input" checked/>
                     <span class="form-imagecheck-figure">
                     <span class="form-imagecheck-figure">
-                      <img src="img/avatars/rus.jpg" alt="Rich Uncle Skeleton" title="Rich Uncle Skeleton" class="form-imagecheck-image" width="100px">
+                      <img src="/images/avatars/rus.jpg" alt="Rich Uncle Skeleton" title="Rich Uncle Skeleton" class="form-imagecheck-image" width="100px">
                     </span>
                     </span>
                   </label>
                   </label>
                 </div>
                 </div>
@@ -96,7 +96,7 @@
                   <label class="form-imagecheck mb-2">
                   <label class="form-imagecheck mb-2">
                     <input name="avatar" type="radio" value="burns.jpg" class="form-imagecheck-input"/>
                     <input name="avatar" type="radio" value="burns.jpg" class="form-imagecheck-input"/>
                     <span class="form-imagecheck-figure">
                     <span class="form-imagecheck-figure">
-                      <img src="img/avatars/burns.jpg" alt="Montgomery Burns" title="Montgomery Burns" class="form-imagecheck-image" width="100px">
+                      <img src="/images/avatars/burns.jpg" alt="Montgomery Burns" title="Montgomery Burns" class="form-imagecheck-image" width="100px">
                     </span>
                     </span>
                   </label>
                   </label>
                 </div>
                 </div>
@@ -104,7 +104,7 @@
                   <label class="form-imagecheck mb-2">
                   <label class="form-imagecheck mb-2">
                     <input name="avatar" type="radio" value="frank.jpg" class="form-imagecheck-input" />
                     <input name="avatar" type="radio" value="frank.jpg" class="form-imagecheck-input" />
                     <span class="form-imagecheck-figure">
                     <span class="form-imagecheck-figure">
-                      <img src="img/avatars/frank.jpg" alt="Frank Grimes" title= "Frank Grimes" class="form-imagecheck-image" width="100px">
+                      <img src="/images/avatars/frank.jpg" alt="Frank Grimes" title= "Frank Grimes" class="form-imagecheck-image" width="100px">
                     </span>
                     </span>
                   </label>
                   </label>
                 </div>
                 </div>
@@ -112,7 +112,7 @@
                   <label class="form-imagecheck mb-2">
                   <label class="form-imagecheck mb-2">
                     <input name="avatar" type="radio" value="moe.jpg" class="form-imagecheck-input"/>
                     <input name="avatar" type="radio" value="moe.jpg" class="form-imagecheck-input"/>
                     <span class="form-imagecheck-figure">
                     <span class="form-imagecheck-figure">
-                      <img src="img/avatars/moe.jpg" alt="Moe Szyslak" title="Moe Szyslak" class="form-imagecheck-image" width="100px">
+                      <img src="/images/avatars/moe.jpg" alt="Moe Szyslak" title="Moe Szyslak" class="form-imagecheck-image" width="100px">
                     </span>
                     </span>
                   </label>
                   </label>
                 </div>
                 </div>
@@ -120,7 +120,7 @@
                   <label class="form-imagecheck mb-2">
                   <label class="form-imagecheck mb-2">
                     <input name="avatar" type="radio" value="poochie.jpg" class="form-imagecheck-input" />
                     <input name="avatar" type="radio" value="poochie.jpg" class="form-imagecheck-input" />
                     <span class="form-imagecheck-figure">
                     <span class="form-imagecheck-figure">
-                      <img src="img/avatars/poochie.jpg" alt="Poochie" title="Poochie" class="form-imagecheck-image" width="100px">
+                      <img src="/images/avatars/poochie.jpg" alt="Poochie" title="Poochie" class="form-imagecheck-image" width="100px">
                     </span>
                     </span>
                   </label>
                   </label>
                 </div>
                 </div>
@@ -128,7 +128,7 @@
                   <label class="form-imagecheck mb-2">
                   <label class="form-imagecheck mb-2">
                     <input name="avatar" type="radio" value="skinner.jpg" class="form-imagecheck-input" />
                     <input name="avatar" type="radio" value="skinner.jpg" class="form-imagecheck-input" />
                     <span class="form-imagecheck-figure">
                     <span class="form-imagecheck-figure">
-                      <img src="img/avatars/skinner.jpg" alt="Seymour Skinner" title="Seymour Skinner" class="form-imagecheck-image" width="100px">
+                      <img src="/images/avatars/skinner.jpg" alt="Seymour Skinner" title="Seymour Skinner" class="form-imagecheck-image" width="100px">
                     </span>
                     </span>
                   </label>
                   </label>
                 </div>
                 </div>
@@ -136,7 +136,7 @@
                   <label class="form-imagecheck mb-2">
                   <label class="form-imagecheck mb-2">
                     <input name="avatar" type="radio" value="moleman.png" class="form-imagecheck-input" />
                     <input name="avatar" type="radio" value="moleman.png" class="form-imagecheck-input" />
                     <span class="form-imagecheck-figure">
                     <span class="form-imagecheck-figure">
-                      <img src="img/avatars/moleman.png" alt="Hans Moleman" title="Hans Moleman" class="form-imagecheck-image" width="100px">
+                      <img src="/images/avatars/moleman.png" alt="Hans Moleman" title="Hans Moleman" class="form-imagecheck-image" width="100px">
                     </span>
                     </span>
                   </label>
                   </label>
                 </div>
                 </div>
@@ -144,7 +144,7 @@
                   <label class="form-imagecheck mb-2">
                   <label class="form-imagecheck mb-2">
                     <input name="avatar" type="radio" value="duffman.png" class="form-imagecheck-input" />
                     <input name="avatar" type="radio" value="duffman.png" class="form-imagecheck-input" />
                     <span class="form-imagecheck-figure">
                     <span class="form-imagecheck-figure">
-                      <img src="img/avatars/duffman.png" alt="Duffman" title="Duffman" class="form-imagecheck-image" width="100px">
+                      <img src="/images/avatars/duffman.png" alt="Duffman" title="Duffman" class="form-imagecheck-image" width="100px">
                     </span>
                     </span>
                   </label>
                   </label>
                 </div>
                 </div>
@@ -176,11 +176,9 @@
               <div class="d-none d-md-flex">
               <div class="d-none d-md-flex">
                 
                 
                 <a href="?theme=dark" class="nav-link px-0 hide-theme-dark" title="Enable dark mode" data-bs-toggle="tooltip" data-bs-placement="bottom">
                 <a href="?theme=dark" class="nav-link px-0 hide-theme-dark" title="Enable dark mode" data-bs-toggle="tooltip" data-bs-placement="bottom">
-                  <!-- Download SVG icon from http://tabler-icons.io/i/moon -->
                   <svg xmlns="http://www.w3.org/2000/svg" class="icon" 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 d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" /></svg>
                   <svg xmlns="http://www.w3.org/2000/svg" class="icon" 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 d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" /></svg>
                 </a>
                 </a>
                 <a href="?theme=light" class="nav-link px-0 hide-theme-light" title="Enable light mode" data-bs-toggle="tooltip" data-bs-placement="bottom">
                 <a href="?theme=light" class="nav-link px-0 hide-theme-light" title="Enable light mode" data-bs-toggle="tooltip" data-bs-placement="bottom">
-                  <!-- Download SVG icon from http://tabler-icons.io/i/sun -->
                   <svg xmlns="http://www.w3.org/2000/svg" class="icon" 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 d="M12 12m-4 0a4 4 0 1 0 8 0a4 4 0 1 0 -8 0" /><path d="M3 12h1m8 -9v1m8 8h1m-9 8v1m-6.4 -15.4l.7 .7m12.1 -.7l-.7 .7m0 11.4l.7 .7m-12.1 -.7l-.7 .7" /></svg>
                   <svg xmlns="http://www.w3.org/2000/svg" class="icon" 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 d="M12 12m-4 0a4 4 0 1 0 8 0a4 4 0 1 0 -8 0" /><path d="M3 12h1m8 -9v1m8 8h1m-9 8v1m-6.4 -15.4l.7 .7m12.1 -.7l-.7 .7m0 11.4l.7 .7m-12.1 -.7l-.7 .7" /></svg>
                 </a>
                 </a>
               </div>
               </div>

+ 2 - 2
views/settings.ejs → views/settings.html

@@ -21,7 +21,7 @@
 	<body >
 	<body >
 		<div class="page">
 		<div class="page">
 		<!-- Navbar -->
 		<!-- Navbar -->
-		<%- include('navbar.ejs') %>
+		<%- include('navbar.html') %>
 		<div class="page-wrapper">
 		<div class="page-wrapper">
 			<!-- Page header -->
 			<!-- Page header -->
 			<div class="page-header d-print-none">
 			<div class="page-header d-print-none">
@@ -125,7 +125,7 @@
 				</div>
 				</div>
 			  </div>
 			  </div>
 
 
-			<%- include('footer.ejs') %>
+			<%- include('footer.html') %>
 		</div>
 		</div>
 		</div>
 		</div>
 		<!-- Libs JS -->
 		<!-- Libs JS -->

+ 2 - 2
views/syslogs.ejs → views/syslogs.html

@@ -21,7 +21,7 @@
     <div class="page">
     <div class="page">
 
 
 
 
-      <%- include('navbar.ejs') %>
+      <%- include('navbar.html') %>
 
 
       <div class="page-wrapper">
       <div class="page-wrapper">
 
 
@@ -68,7 +68,7 @@
 
 
           </div>
           </div>
         </div>
         </div>
-        <%- include('footer.ejs') %>
+        <%- include('footer.html') %>
       </div>
       </div>
     </div>
     </div>
     <!-- Libs JS -->
     <!-- Libs JS -->

+ 2 - 2
views/users.ejs → views/users.html

@@ -21,7 +21,7 @@
     <div class="page">
     <div class="page">
 
 
 
 
-      <%- include('navbar.ejs') %>
+      <%- include('navbar.html') %>
 
 
       <div class="page-wrapper">
       <div class="page-wrapper">
 
 
@@ -59,7 +59,7 @@
             </div>
             </div>
           </div>
           </div>
         </div>
         </div>
-        <%- include('footer.ejs') %>
+        <%- include('footer.html') %>
       </div>
       </div>
     </div>
     </div>
     <!-- Libs JS -->
     <!-- Libs JS -->

+ 2 - 2
views/volumes.ejs → views/volumes.html

@@ -20,7 +20,7 @@
   <body >
   <body >
     <div class="page">
     <div class="page">
       <!-- Navbar -->
       <!-- Navbar -->
-      <%- include('navbar.ejs') %>
+      <%- include('navbar.html') %>
       <div class="page-wrapper">
       <div class="page-wrapper">
         <!-- Page header -->
         <!-- Page header -->
         
         
@@ -144,7 +144,7 @@
           </div>
           </div>
         </div>
         </div>
         
         
-        <%- include('footer.ejs') %>
+        <%- include('footer.html') %>
         
         
       </div>
       </div>
     </div>
     </div>

Vissa filer visades inte eftersom för många filer har ändrats