moved routes into dashboard.js

This commit is contained in:
lllllllillllllillll 2024-02-14 01:45:29 -08:00
parent 04cc1c1df3
commit 97481b0b75
7 changed files with 286 additions and 217 deletions

View file

@ -54,16 +54,16 @@ export const containerCard = (data) => {
<div class="ms-auto lh-1">
<div class="card-actions btn-actions">
<div class="card-actions btn-actions">
<button class="btn-action" title="Start" data-hx-get="/action" data-hx-trigger="click" data-hx-swap="none" name="${name}" id="start" value="${state}" ${disable}><!-- player-play -->
<button class="btn-action" title="Start" data-hx-post="/start" data-hx-trigger="click" data-hx-swap="none" name="${name}" id="${state}" ${disable}><!-- 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 class="btn-action" title="Stop" data-hx-get="/action" data-hx-trigger="click" data-hx-swap="none" name="${name}" id="stop" value="${state}" ${disable}><!-- player-stop -->
<button class="btn-action" title="Stop" data-hx-post="/stop" data-hx-trigger="click" data-hx-swap="none" name="${name}" id="${state}" ${disable}><!-- 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 class="btn-action" title="Pause" data-hx-get="/action" data-hx-trigger="click" data-hx-swap="none" name="${name}" id="pause" value="${state}" ${disable}><!-- player-pause -->
<button class="btn-action" title="Pause" data-hx-post="/pause" data-hx-trigger="click" data-hx-swap="none" name="${name}" id="${state}" ${disable}><!-- 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 class="btn-action" title="Restart" data-hx-get="/action" data-hx-trigger="click" data-hx-swap="none" name="${name}" id="restart" value="${state}" ${disable}><!-- reload -->
<button class="btn-action" title="Restart" data-hx-post="/restart" data-hx-trigger="click" data-hx-swap="none" name="${name}" id="${state}" ${disable}><!-- 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">

View file

@ -1,23 +1,244 @@
import { docker, event } from "../server.js";
import { Readable } from 'stream';
import { Permission, Container } from '../database/models.js';
import { modal } from '../components/modal.js';
import { permissionsModal } from '../components/permissions_modal.js';
import { cpu, ram, tx, rx, disk } from '../server.js';
import { dockerContainerStats } from 'systeminformation';
export const Dashboard = (req, res) => {
res.render("dashboard", {
name: req.session.user,
role: req.session.role,
avatar: req.session.avatar,
});
}
export const searchDashboard = (req, res) => {
// console.log(req.params);
res.render("dashboard", {
name: req.session.user,
role: req.session.role,
avatar: req.session.avatar,
});
}
export const Start = (req, res) => {
let name = req.header('hx-trigger-name');
let state = req.header('hx-trigger');
if (state == 'stopped') {
var containerName = docker.getContainer(name);
containerName.start();
} else if (state == 'paused') {
var containerName = docker.getContainer(name);
containerName.unpause();
}
res.send("ok");
}
export const Stop = (req, res) => {
console.log(`Clicked on stop`);
let name = req.header('hx-trigger-name');
let state = req.header('hx-trigger');
if (state != 'stopped') {
var containerName = docker.getContainer(name);
containerName.stop();
}
res.send("ok");
}
export const Pause = (req, res) => {
console.log(`Clicked on pause`);
let name = req.header('hx-trigger-name');
let state = req.header('hx-trigger');
if (state == 'running') {
var containerName = docker.getContainer(name);
containerName.pause();
} else if (state == 'paused') {
var containerName = docker.getContainer(name);
containerName.unpause();
}
res.send("ok");
}
export const Restart = (req, res) => {
console.log(`Clicked on restart`);
let name = req.header('hx-trigger-name');
var containerName = docker.getContainer(name);
containerName.restart();
res.send("ok");
}
export const Logs = (req, res) => {
let name = req.header('hx-trigger-name');
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) => {
res.send(`<pre>${data}</pre> `)
});
}
export const Modal = async (req, res) => {
let name = req.header('hx-trigger-name');
let id = req.header('hx-trigger');
if (id == 'permissions') {
let containerPermissions = await Permission.findAll({ where: {containerName: name}});
let form = permissionsModal();
res.send(form);
return;
}
let containerId = docker.getContainer(name);
let containerInfo = await containerId.inspect();
let ports_list = [];
try {
for (const [key, value] of Object.entries(containerInfo.HostConfig.PortBindings)) {
let ports = {
check: 'checked',
external: value[0].HostPort,
internal: key.split('/')[0],
protocol: key.split('/')[1]
}
ports_list.push(ports);
}
} catch {}
let external_port = ports_list[0]?.external || 0;
let internal_port = ports_list[0]?.internal || 0;
let container_info = {
name: containerInfo.Name.slice(1),
state: containerInfo.State.Status,
image: containerInfo.Config.Image,
external_port: external_port,
internal_port: internal_port,
ports: ports_list,
link: 'localhost',
}
let form = modal(container_info);
res.send(form);
}
export const searchDashboard = (req, res) => {
// console.log(req.params);
export const Stats = async (req, res) => {
let name = req.header('hx-trigger-name');
let color = req.header('hx-trigger');
let value = 0;
switch (name) {
case 'CPU': value = cpu;
break;
case 'RAM': value = ram;
break;
case 'TX': value = tx;
break;
case 'RX': value = rx;
break;
case 'DISK': value = disk;
break;
}
let info = `<div class="font-weight-medium">
<label class="cpu-text mb-1">${name} ${value}%</label>
</div>
<div class="cpu-bar meter animate ${color}">
<span style="width:${value}%"><span></span></span>
</div>`;
res.send(info);
}
res.render("dashboard", {
name: req.session.user,
role: req.session.role,
avatar: req.session.avatar,
});
export const Hide = async (req, res) => {
let name = req.header('hx-trigger-name');
let id = req.header('hx-trigger');
let exists = await Container.findOne({ where: {name: name}});
if (!exists) {
const newContainer = await Container.create({ name: name, visibility: false, });
} else {
exists.update({ visibility: false });
}
event = true;
eventInfo = 'docker';
res.send("ok");
}
export const Reset = async (req, res) => {
Container.update({ visibility: true }, { where: {} });
event = true;
eventInfo = 'docker';
res.send("ok");
}
let stats = {};
export const Chart = async (req, res) => {
let name = req.header('hx-trigger-name');
// create an empty array if it doesn't exist
if (!stats[name]) {
stats[name] = { cpuArray: Array(15).fill(0), ramArray: Array(15).fill(0) };
}
// get the stats
const info = await dockerContainerStats(name);
// update the arrays
stats[name].cpuArray.push(Math.round(info[0].cpuPercent));
stats[name].ramArray.push(Math.round(info[0].memPercent));
// slice them down to the last 15 values
stats[name].cpuArray = stats[name].cpuArray.slice(-15);
stats[name].ramArray = stats[name].ramArray.slice(-15);
// replace the chart with the new data
let chart = `
<script>
${name}chart.updateSeries([{
data: [${stats[name].cpuArray}]
}, {
data: [${stats[name].ramArray}]
}])
</script>`
res.send(chart);
}

View file

@ -19,3 +19,14 @@ export const Supporters = async (req, res) => {
}
let thanks = 0;
export const Thanks = async (req, res) => {
thanks++;
let data = thanks.toString();
if (thanks > 999) {
data = 'Did you really click 1000 times?!';
}
res.send(data);
}

View file

@ -4,7 +4,7 @@ export const router = express.Router();
// Controllers
import { Login, submitLogin, Logout } from "../controllers/login.js";
import { Register, submitRegister } from "../controllers/register.js";
import { Dashboard, searchDashboard } from "../controllers/dashboard.js";
import { Dashboard, searchDashboard, Start, Stop, Pause, Restart, Logs, Modal, Stats, Hide, Reset, Chart } from "../controllers/dashboard.js";
import { Apps, appSearch } from "../controllers/apps.js";
import { Users } from "../controllers/users.js";
import { Images, removeImage } from "../controllers/images.js";
@ -13,7 +13,7 @@ import { Volumes, removeVolume } from "../controllers/volumes.js";
import { Account } from "../controllers/account.js";
import { Variables } from "../controllers/variables.js";
import { Settings } from "../controllers/settings.js";
import { Supporters } from "../controllers/supporters.js";
import { Supporters, Thanks } from "../controllers/supporters.js";
import { Syslogs } from "../controllers/syslogs.js";
import { Portal } from "../controllers/portal.js"
@ -29,13 +29,22 @@ const auth = (req, res, next) => {
// Routes
router.get("/login", Login);
router.post("/login", submitLogin);
router.get("/logout", Logout);
router.get("/register", Register);
router.post("/register", submitRegister);
router.get("/logout", Logout);
router.get("/", auth, Dashboard);
router.post("/", auth, searchDashboard);
router.post("/start", auth, Start);
router.post("/stop", auth, Stop);
router.post("/pause", auth, Pause);
router.post("/restart", auth, Restart);
router.get("/logs", auth, Logs);
router.get ("/modal", auth, Modal);
router.get("/stats", auth, Stats);
router.post("/hide", auth, Hide);
router.post("/reset", auth, Reset);
router.get("/chart", auth, Chart);
router.get("/images", auth, Images);
router.post("/removeImage", removeImage);
@ -58,7 +67,9 @@ router.get("/syslogs", auth, Syslogs);
router.get("/account", Account);
router.get("/variables", auth, Variables);
router.get("/settings", auth, Settings);
router.get("/supporters", Supporters);
router.post("/thank", Thanks);
// Functions

212
server.js
View file

@ -3,15 +3,14 @@ import session from 'express-session';
import memorystore from 'memorystore';
import ejs from 'ejs';
import Docker from 'dockerode';
import { Readable } from 'stream';
import { router } from './router/index.js';
import { sequelize, Container, Permission } from './database/models.js';
import { currentLoad, mem, networkStats, fsSize, dockerContainerStats, dockerImages, networkInterfaces } from 'systeminformation';
import { sequelize, Container } from './database/models.js';
import { currentLoad, mem, networkStats, fsSize } from 'systeminformation';
import { containerCard } from './components/containerCard.js';
import { modal } from './components/modal.js';
import { permissionsModal } from './components/permissions_modal.js';
export var docker = new Docker();
export { event, sse, cpu, ram, tx, rx, disk }
const app = express();
const MemoryStore = memorystore(session);
const port = process.env.PORT || 8000;
@ -56,8 +55,6 @@ app.listen(port, async () => {
let [ cpu, ram, tx, rx, disk ] = [0, 0, 0, 0, 0];
let [ hidden, cardList, sentList ] = ['', '', ''];
let thanks = 0;
let event = false;
let sse = false;
let eventInfo = '';
@ -80,38 +77,6 @@ let serverMetrics = async () => {
}
setInterval(serverMetrics, 1000);
router.get('/stats', async (req, res) => {
let name = req.header('hx-trigger-name');
let color = req.header('hx-trigger');
let value = 0;
switch (name) {
case 'CPU':
value = cpu;
break;
case 'RAM':
value = ram;
break;
case 'TX':
value = tx;
break;
case 'RX':
value = rx;
break;
case 'DISK':
value = disk;
break;
}
let info = `<div class="font-weight-medium">
<label class="cpu-text mb-1">${name} ${value}%</label>
</div>
<div class="cpu-bar meter animate ${color}">
<span style="width:${value}%"><span></span></span>
</div>`;
res.send(info);
});
// Get hidden containers
async function getHidden() {
hidden = await Container.findAll({ where: {visibility:false}});
@ -192,175 +157,36 @@ router.get('/containers', async (req, res) => {
res.send(cardList);
});
// Dashboard controls
router.get('/action', async (req, res) => {
let name = req.header('hx-trigger-name');
let id = req.header('hx-trigger');
let value = req.query[name];
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();
}
});
router.get('/hide', async (req, res) => {
let name = req.header('hx-trigger-name');
let id = req.header('hx-trigger');
if (id == 'hide') {
let exists = await Container.findOne({ where: {name: name}});
if (!exists) {
const newContainer = await Container.create({ name: name, visibility: false, });
} else {
exists.update({ visibility: false });
}
} else if (id == 'reset') {
Container.update({ visibility: true }, { where: {} });
}
event = true;
eventInfo = 'docker';
});
router.get('/logs', async (req, res) => {
let name = req.header('hx-trigger-name');
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) => {
res.send(`<pre>${data}</pre> `)
});
});
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;
console.log(`event: ${eventInfo}`);
res.write(`event: ${eventInfo}\n`);
res.write(`data: there was an event!\n\n`);
sse = false;
}
}, 1000);
req.on('close', () => {
clearInterval(eventCheck);
});
return;
});
router.get('/modal', async (req, res) => {
let name = req.header('hx-trigger-name');
let id = req.header('hx-trigger');
if (id == 'permissions') {
let containerPermissions = await Permission.findAll({ where: {containerName: name}});
let form = permissionsModal();
res.send(form);
return;
}
let containerId = docker.getContainer(name);
let containerInfo = await containerId.inspect();
let ports_list = [];
try {
for (const [key, value] of Object.entries(containerInfo.HostConfig.PortBindings)) {
let ports = {
check: 'checked',
external: value[0].HostPort,
internal: key.split('/')[0],
protocol: key.split('/')[1]
}
ports_list.push(ports);
}
} catch {}
let external_port = ports_list[0]?.external || 0;
let internal_port = ports_list[0]?.internal || 0;
let container_info = {
name: containerInfo.Name.slice(1),
state: containerInfo.State.Status,
image: containerInfo.Config.Image,
external_port: external_port,
internal_port: internal_port,
ports: ports_list,
router.get('/installing', async (req, res) => {
let install_info = {
name: 'App Name',
service: '',
id: '',
state: 'Installing',
image: '',
external_port: 0,
internal_port: 0,
ports: '',
link: 'localhost',
}
let form = modal(container_info);
res.send(form);
});
let stats = {};
router.get('/chart', async (req, res) => {
let name = req.header('hx-trigger-name');
// create an empty array if it doesn't exist
if (!stats[name]) {
stats[name] = { cpuArray: Array(15).fill(0), ramArray: Array(15).fill(0) };
}
// get the stats
const info = await dockerContainerStats(name);
// update the arrays
stats[name].cpuArray.push(Math.round(info[0].cpuPercent));
stats[name].ramArray.push(Math.round(info[0].memPercent));
// slice them down to the last 15 values
stats[name].cpuArray = stats[name].cpuArray.slice(-15);
stats[name].ramArray = stats[name].ramArray.slice(-15);
// replace the chart with the new data
let chart = `
<script>
${name}chart.updateSeries([{
data: [${stats[name].cpuArray}]
}, {
data: [${stats[name].ramArray}]
}])
</script>`
res.send(chart);
});
router.get('/thank', async (req, res) => {
thanks++;
let data = thanks.toString();
if (thanks > 999) {
data = 'Did you really click 1000 times?!';
}
res.send(data);
let card = containerCard(install_info);
res.send(card);
});

View file

@ -22,7 +22,7 @@
</head>
<body >
<div class="page">
<div class="page" hx-ext="sse" sse-connect="/sse_event">
<%- include('navbar.html') %>
@ -137,15 +137,15 @@
</div>
<!-- HTMX -->
<div class="col-12" hx-ext="sse" sse-connect="/sse_event">
<div class="col-12">
<div class="row row-cards" data-hx-get="/containers" data-hx-trigger="load, sse:docker" data-hx-swap="innerHTML">
</div>
</div>
<!-- HTMX -->
<div class="col-12" hx-ext="sse" sse-connect="/sse_event">
<div class="row row-cards" data-hx-get="/installing" data-hx-trigger="load, sse:install" data-hx-swap="innerHTML">
<div class="col-12">
<div class="row row-cards" data-hx-get="/installing" data-hx-trigger="sse:install" data-hx-swap="innerHTML">
</div>
</div>

View file

@ -50,9 +50,9 @@
<div class="row align-items-center">
<div class="col">
<span type="button" class="avatar avatar-md bg-green-lt" hx-trigger="load, click" hx-get="/thank" hx-target="#count" name="MM" title="Micheal M" style="margin-right: 5px;">mm</span>
<span type="button" class="avatar avatar-md bg-green-lt" hx-trigger="load, click" hx-post="/thank" hx-target="#count" name="MM" title="Micheal M" style="margin-right: 5px;">mm</span>
<span type="button" class="avatar avatar-md bg-cyan-lt" hx-trigger="click" hx-get="/thank" hx-target="#count" name="PD" title="Peter D" style="margin-right: 5px;">pd</span>
<span type="button" class="avatar avatar-md bg-cyan-lt" hx-trigger="click" hx-post="/thank" hx-target="#count" name="PD" title="Peter D" style="margin-right: 5px;">pd</span>
</div>