updated dependencies and updated router auth
This commit is contained in:
parent
42ca573b51
commit
c9d7dea132
|
@ -4,7 +4,7 @@
|
|||
<p align="center">
|
||||
<a href=""><img src="https://img.shields.io/github/stars/lllllllillllllillll/DweebUI?style=flat"/></a>
|
||||
<a href="https://github.com/lllllllillllllillll"><img src="https://img.shields.io/github/commit-activity/y/lllllllillllllillll/DweebUI%2Fdev"/></a>
|
||||
<a href="https://github.com/lllllllillllllillll/DweebUI"><img src="https://img.shields.io/github/last-commit/lllllllillllllillll/DweebUI%2Fdev"/></a>
|
||||
<a href="https://github.com/lllllllillllllillll/DweebUI%2Fdev"><img src="https://img.shields.io/github/last-commit/lllllllillllllillll/DweebUI%2Fdev"/></a>
|
||||
<a href="https://hub.docker.com/r/lllllllillllllillll/dweebui"><img src="https://img.shields.io/docker/pulls/lllllllillllllillll/dweebui"/></a>
|
||||
<a href="https://github.com/lllllllillllllillll/DweebUI/blob/main/LICENSE"><img src="https://img.shields.io/github/license/lllllllillllllillll/DweebUI"/></a>
|
||||
<a href="https://www.reddit.com/r/dweebui"><img src="https://img.shields.io/badge/reddit-orange"/></a>
|
||||
|
@ -27,7 +27,7 @@
|
|||
* [x] Light/Dark Mode.
|
||||
* [x] Mobile Friendly.
|
||||
* [x] Easy to install app templates.
|
||||
* [x] Multi-User built-in.
|
||||
* [x] Multiple Users.
|
||||
* [ ] Permissions system (in development).
|
||||
* [x] Support for Windows, Linux, and MacOS.
|
||||
* [ ] Docker compose (in development).
|
||||
|
|
|
@ -3,7 +3,6 @@ import { Permission, Container, User } from '../database/models.js';
|
|||
import { docker } from '../server.js';
|
||||
import { dockerContainerStats } from 'systeminformation';
|
||||
import { readFileSync } from 'fs';
|
||||
import { currentLoad, mem, networkStats, fsSize } from 'systeminformation';
|
||||
|
||||
let hidden = '';
|
||||
|
||||
|
@ -21,6 +20,7 @@ export const Portal = (req, res) => {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
async function containerInfo (containerName) {
|
||||
let container = docker.getContainer(containerName);
|
||||
let info = await container.inspect();
|
||||
|
@ -126,6 +126,72 @@ export async function addCard (name, state) {
|
|||
|
||||
|
||||
|
||||
|
||||
// HTMX server-side events
|
||||
export const SSE = (req, res) => {
|
||||
res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive' });
|
||||
|
||||
let eventCheck = setInterval(async () => {
|
||||
// builds array of containers and their states
|
||||
containersArray = [];
|
||||
await docker.listContainers({ all: true }).then(containers => {
|
||||
containers.forEach(container => {
|
||||
let name = container.Names[0].replace('/', '');
|
||||
if (!hidden.includes(name)) { // if not hidden
|
||||
containersArray.push({ container: name, state: container.State });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if ((JSON.stringify(containersArray) !== JSON.stringify(sentArray))) {
|
||||
cardList = '';
|
||||
newCards = '';
|
||||
containersArray.forEach(container => {
|
||||
const { container: containerName, state } = container;
|
||||
const existingContainer = sentArray.find(c => c.container === containerName);
|
||||
if (!existingContainer) {
|
||||
containerInfo(containerName).then(details => {
|
||||
createCard(details).then(card => {
|
||||
newCards += card;
|
||||
});
|
||||
});
|
||||
res.write(`event: update\n`);
|
||||
res.write(`data: 'update cards'\n\n`);
|
||||
} else if (existingContainer.state !== state) {
|
||||
updatesArray.push(containerName);
|
||||
}
|
||||
containerInfo(containerName).then(details => {
|
||||
createCard(details).then(card => {
|
||||
cardList += card;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
sentArray.forEach(container => {
|
||||
const { container: containerName } = container;
|
||||
const existingContainer = containersArray.find(c => c.container === containerName);
|
||||
if (!existingContainer) {
|
||||
updatesArray.push(containerName);
|
||||
}
|
||||
});
|
||||
|
||||
for (let i = 0; i < updatesArray.length; i++) {
|
||||
res.write(`event: ${updatesArray[i]}\n`);
|
||||
res.write(`data: 'update cards'\n\n`);
|
||||
}
|
||||
updatesArray = [];
|
||||
sentArray = containersArray.slice();
|
||||
}
|
||||
|
||||
}, 500);
|
||||
|
||||
|
||||
req.on('close', () => {
|
||||
clearInterval(eventCheck);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
export const updateCards = async (req, res) => {
|
||||
console.log('updateCards called');
|
||||
res.send(newCards);
|
||||
|
@ -152,8 +218,6 @@ export const Card = async (req, res) => {
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function status (state) {
|
||||
let status = `<span class="text-yellow align-items-center lh-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon-tabler icon-tabler-point-filled" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M12 7a5 5 0 1 1 -4.995 5.217l-.005 -.217l.005 -.217a5 5 0 0 1 4.995 -4.783z" stroke-width="0" fill="currentColor"></path></svg>
|
||||
|
|
|
@ -13,13 +13,13 @@
|
|||
"dockerode": "^4.0.2",
|
||||
"dockerode-compose": "^1.4.0",
|
||||
"ejs": "^3.1.9",
|
||||
"express": "^4.18.3",
|
||||
"express": "^4.19.2",
|
||||
"express-session": "^1.18.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"memorystore": "^1.6.7",
|
||||
"sequelize": "^6.37.1",
|
||||
"sqlite3": "^5.1.7",
|
||||
"systeminformation": "^5.22.0"
|
||||
"systeminformation": "^5.22.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@balena/dockerignore": {
|
||||
|
@ -525,9 +525,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/cookie": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
|
||||
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
|
||||
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
|
@ -810,16 +810,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "4.18.3",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.18.3.tgz",
|
||||
"integrity": "sha512-6VyCijWQ+9O7WuVMTRBTl+cjNNIzD5cY5mQ1WM8r/LEkI2u8EYpOotESNwzNlyCn3g+dmjKYI6BmNneSr/FSRw==",
|
||||
"version": "4.19.2",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
|
||||
"integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.8",
|
||||
"array-flatten": "1.1.1",
|
||||
"body-parser": "1.20.2",
|
||||
"content-disposition": "0.5.4",
|
||||
"content-type": "~1.0.4",
|
||||
"cookie": "0.5.0",
|
||||
"cookie": "0.6.0",
|
||||
"cookie-signature": "1.0.6",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
|
@ -868,14 +868,6 @@
|
|||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/express-session/node_modules/cookie": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
|
||||
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/express-session/node_modules/cookie-signature": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz",
|
||||
|
@ -2514,9 +2506,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/systeminformation": {
|
||||
"version": "5.22.0",
|
||||
"resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.22.0.tgz",
|
||||
"integrity": "sha512-oAP80ymt8ssrAzjX8k3frbL7ys6AotqC35oikG6/SG15wBw+tG9nCk4oPaXIhEaAOAZ8XngxUv3ORq2IuR3r4Q==",
|
||||
"version": "5.22.6",
|
||||
"resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.22.6.tgz",
|
||||
"integrity": "sha512-hUTQX+bRgIFbv1T/z251NtwGwNIeSyWURnT2BGnsYu6dQNbkiBl4oAwk50acVfITFq1Zvb8KDNgibQK9uGlUGg==",
|
||||
"os": [
|
||||
"darwin",
|
||||
"linux",
|
||||
|
|
|
@ -16,12 +16,12 @@
|
|||
"dockerode": "^4.0.2",
|
||||
"dockerode-compose": "^1.4.0",
|
||||
"ejs": "^3.1.9",
|
||||
"express": "^4.18.3",
|
||||
"express": "^4.19.2",
|
||||
"express-session": "^1.18.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"memorystore": "^1.6.7",
|
||||
"sequelize": "^6.37.1",
|
||||
"sqlite3": "^5.1.7",
|
||||
"systeminformation": "^5.22.0"
|
||||
"systeminformation": "^5.22.6"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,51 +22,55 @@ import { Portal } from "../controllers/portal.js"
|
|||
// Auth middleware
|
||||
const auth = async (req, res, next) => {
|
||||
if (!req.session.user) { res.redirect('/login'); return; }
|
||||
if (req.session.role == "admin") { next(); }
|
||||
|
||||
let user = req.session.user;
|
||||
let role = req.session.role;
|
||||
let action = req.path.split("/")[2];
|
||||
let trigger = req.header('hx-trigger-name');
|
||||
|
||||
// console.log("Auth: ", user, role, action, trigger);
|
||||
|
||||
if (role == "admin") {
|
||||
next();
|
||||
}
|
||||
else if (action == "start" || action == "stop" || action == "pause" || action == "restart") {
|
||||
let permission = await Permission.findOne({ where: { containerName: trigger, user: user }, attributes: [`${action}`] });
|
||||
|
||||
// if (action == "start" || action == "stop" || action == "pause" || action == "restart") {
|
||||
// let permission = await Permission.findOne({ where: { containerName: trigger, user: user }, attributes: [`${action}`] });
|
||||
|
||||
if (permission) {
|
||||
if (permission[action] == true) {
|
||||
console.log(`User ${user} has permission to ${action} ${trigger}`);
|
||||
next();
|
||||
}
|
||||
else {
|
||||
console.log(`User ${user} does not have permission to ${action} ${trigger}`);
|
||||
}
|
||||
} else {
|
||||
console.log(`No entry found for ${user} in ${trigger} permissions`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
res.redirect('/portal');
|
||||
}
|
||||
// if (permission) {
|
||||
// if (permission[action] == true) {
|
||||
// console.log(`User ${user} has permission to ${action} ${trigger}`);
|
||||
// next();
|
||||
// }
|
||||
// else {
|
||||
// console.log(`User ${user} does not have permission to ${action} ${trigger}`);
|
||||
// }
|
||||
// } else {
|
||||
// console.log(`No entry found for ${user} in ${trigger} permissions`);
|
||||
// }
|
||||
// }
|
||||
// else {
|
||||
// res.redirect('/portal');
|
||||
// }
|
||||
|
||||
res.redirect('/portal');
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Admin routes
|
||||
router.get("/", auth, Dashboard);
|
||||
router.post("/action/:action", auth, Action);
|
||||
router.post("/updatePermissions", auth, UpdatePermissions);
|
||||
|
||||
router.get("/logs", Logs);
|
||||
|
||||
router.get("/modals", Modals);
|
||||
router.get("/logs", auth, Logs);
|
||||
router.get("/modals", auth, Modals);
|
||||
router.get("/stats", auth, Stats);
|
||||
router.get("/chart", auth, Chart);
|
||||
router.get("/sse_event", auth, SSE);
|
||||
router.get("/containers", Containers);
|
||||
router.get("/card", Card);
|
||||
router.get("/new_cards", updateCards);
|
||||
router.get("/containers", auth, Containers);
|
||||
router.get("/card", auth, Card);
|
||||
router.get("/new_cards", auth, updateCards);
|
||||
|
||||
|
||||
router.get("/images", auth, Images);
|
||||
|
|
|
@ -61,6 +61,16 @@
|
|||
VNC
|
||||
</a>
|
||||
</div> -->
|
||||
|
||||
<!-- <% if(role == 'admin') { %>
|
||||
<div class="btn-list">
|
||||
<a href="#" class="btn text-red">
|
||||
Admin
|
||||
</a>
|
||||
</div>
|
||||
<% } %> -->
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
<link href="/css/tabler.min.css" rel="stylesheet"/>
|
||||
<link href="/css/meters.css" rel="stylesheet"/>
|
||||
<script src="/js/htmx.min.js"></script>
|
||||
<script src="/js/htmx-sse.js"></script>
|
||||
<style>
|
||||
@import url('/fonts/inter.css');
|
||||
:root {
|
||||
|
@ -29,7 +30,7 @@
|
|||
|
||||
<div class="page-body">
|
||||
<div class="container-xl">
|
||||
<div class="row row-deck row-cards">
|
||||
<div class="row row-deck row-cards" hx-ext="sse" sse-connect="/sse_event">
|
||||
|
||||
<div class="col-12">
|
||||
<div class="row row-cards">
|
||||
|
@ -45,7 +46,7 @@
|
|||
</div>
|
||||
|
||||
<!-- HTMX -->
|
||||
<div class="col" name="CPU" id="green" data-hx-get="" data-hx-trigger="">
|
||||
<div class="col" name="CPU" id="green" data-hx-get="/stats" data-hx-trigger="load, every 1s">
|
||||
<div class="font-weight-medium">
|
||||
<label class="cpu-text mb-1" for="cpu">CPU 0%</label>
|
||||
</div>
|
||||
|
@ -70,7 +71,7 @@
|
|||
</div>
|
||||
|
||||
<!-- HTMX -->
|
||||
<div class="col" name="RAM" id="blue" data-hx-get="" data-hx-trigger="">
|
||||
<div class="col" name="RAM" id="blue" data-hx-get="/stats" data-hx-trigger="load, every 2s">
|
||||
<div class="font-weight-medium">
|
||||
<label class="ram-text mb-1" for="ram">RAM 0%</label>
|
||||
</div>
|
||||
|
@ -95,7 +96,7 @@
|
|||
</div>
|
||||
|
||||
<!-- HTMX -->
|
||||
<div class="col" name="NET" id="purple" data-hx-get="" data-hx-trigger="">
|
||||
<div class="col" name="NET" id="purple" data-hx-get="/stats" data-hx-trigger="load, every 2s">
|
||||
<div class="font-weight-medium">
|
||||
<label id="net-text" class="net-text mb-1" for="network">Down: 0MB Up: 0MB</label>
|
||||
</div>
|
||||
|
@ -120,7 +121,7 @@
|
|||
</div>
|
||||
|
||||
<!-- HTMX -->
|
||||
<div class="col" name="DISK" id="orange" data-hx-get="" data-hx-trigger="">
|
||||
<div class="col" name="DISK" id="orange" data-hx-get="/stats" data-hx-trigger="load, every 3s">
|
||||
<div class="font-weight-medium">
|
||||
<label class="disk-text mb-1" for="disk">DISK 0%</label>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue