Merge pull request #3 from lllllllillllllillll/dev

0.02
This commit is contained in:
lllllllillllllillll 2023-11-01 00:54:17 -07:00 committed by GitHub
commit e2463ebc47
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 900 additions and 330 deletions

174
app.js
View file

@ -1,15 +1,12 @@
const express = require("express");
const session = require("express-session");
const redis = require('connect-redis');
const { currentLoad, mem, networkStats, fsSize, dockerContainerStats } = require('systeminformation');
const app = express();
const routes = require("./routes");
const PORT = 8000;
var Docker = require('dockerode');
var docker = new Docker({ socketPath: '/var/run/docker.sock' });
const { dashCard } = require('./components/dashCard');
let DockerContainers, sent_list, clicked, open_ports, ServerMetrics, card_list, external_port, internal_port;
const { serverStats, containerList, containerStats, containerAction } = require('./functions/system_information');
let sent_list, clicked;
const redisClient = require('redis').createClient({
legacyMode:true
@ -38,143 +35,66 @@ app.use([
routes
]);
const server = app.listen(PORT, async () => {
console.log(`App listening on port ${PORT}`);
const server = app.listen(8000, async () => {
console.log(`App listening on port 8000`);
});
const io = require('socket.io')(server);
io.engine.use(sessionMiddleware);
io.on('connection', (socket) => {
// set user session
const user_session = socket.request.session;
console.log(`${user_session.user} connected from ${socket.handshake.headers.host} ${socket.handshake.address}`);
// display client connection info
console.log(`${user_session.user} connected from ${socket.handshake.headers.host} ${socket.handshake.address} \n Active Sessions: ${io.engine.clientsCount}`);
// send list of running docker containers if sent_list contains data
// check if a list of containers needs to be sent
if (sent_list != null) { socket.emit('cards', sent_list); }
// check if an install is in progress
if((app.locals.install != '') && (app.locals.install != null)){
socket.emit('install', app.locals.install);
}
// check if an install card has to be sent
if((app.locals.install != '') && (app.locals.install != null)){ socket.emit('install', app.locals.install); }
// send server metrics to client
async function Metrics() {
Promise.all([currentLoad(), mem(), networkStats(), fsSize()]).then(([cpuUsage, ramUsage, netUsage, diskUsage]) => {
let cpu = Math.round(cpuUsage.currentLoad);
let ram = Math.round(((ramUsage.active / ramUsage.total) * 100));
let tx = netUsage[0].tx_bytes;
let rx = netUsage[0].rx_bytes;
let disk = diskUsage[0].use;
socket.emit('metrics', { cpu, ram, tx, rx, disk });
});
}
// send server metrics
let ServerStats = setInterval(async () => {
socket.emit('metrics', await serverStats());
}, 1000);
async function ContainersList() {
card_list = '';
open_ports = '';
external_port;
internal_port;
docker.listContainers({ all: true }, async function (err, data) {
for (const container of data) {
let imageVersion = container.Image.split('/');
let dockerService = imageVersion[imageVersion.length - 1].split(":")[0];
let containerId = docker.getContainer(container.Id);
let containerInfo = await containerId.inspect();
// console.log(containerInfo.Name.split('/')[1]);
// console.log(container.Image);
// console.log(containerInfo.HostConfig.RestartPolicy.Name);
for (const [key, value] of Object.entries(containerInfo.HostConfig.PortBindings)) {
console.log(`${value[0].HostPort}:${key}`);
external_port = value[0].HostPort;
internal_port = key;
}
// console.log('Volumes:');
// for (const [key, value] of Object.entries(containerInfo.Mounts)) {
// console.log(`${value.Source}: ${value.Destination}: ${value.RW}`);
// }
// console.log('Environment Variables:');
// for (const [key, value] of Object.entries(containerInfo.Config.Env)) {
// console.log(`${key}: ${value}`);
// }
// console.log('Labels:');
// for (const [key, value] of Object.entries(containerInfo.Config.Labels)) {
// console.log(`${key}: ${value}`);
// }
// dockerContainerStats(container.Id).then((data) => {
// console.log(`${container.Names[0].slice(1)} // CPU: ${Math.round(data[0].cpuPercent)} // RAM: ${Math.round(data[0].memPercent)}`);
// });
let dockerCard = dashCard(container.Names[0].slice(1), dockerService, container.Id, container.State, container.Image, external_port, internal_port);
// open_ports += `-L ${external_port}:localhost:${external_port} `
card_list += dockerCard;
}
// emit card list is it's different from what was sent last time, then clear install local
if (sent_list !== card_list) {
sent_list = card_list;
app.locals.install = '';
socket.emit('cards', card_list);
console.log('Cards updated');
}
});
}
console.log('Starting Metrics');
ServerMetrics = setInterval(Metrics, 1000);
console.log('Starting Containers List');
DockerContainers = setInterval(ContainersList, 1000);
socket.on('clicked', (data) => {
// Prevent multiple clicks
if (clicked == true) { return; } clicked = true;
console.log(`${socket.request.session.user} wants to: ${data.action} ${data.container}`);
if (socket.request.session.role == 'admin') {
var containerName = docker.getContainer(data.container);
if ((data.action == 'start') && (data.state == 'stopped')) {
containerName.start();
} else if ((data.action == 'start') && (data.state == 'paused')) {
containerName.unpause();
} else if ((data.action == 'stop') && (data.state != 'stopped')) {
containerName.stop();
} else if ((data.action == 'pause') && (data.state == 'running')) {
containerName.pause();
} else if ((data.action == 'pause') && (data.state == 'paused')) {
containerName.unpause();
} else if (data.action == 'restart') {
containerName.restart();
}
} else {
console.log('User is not an admin');
// send container list
let ContainerList = setInterval(async () => {
let card_list = await containerList();
if (sent_list !== card_list) {
sent_list = card_list;
app.locals.install = '';
socket.emit('cards', card_list);
}
}, 1000);
// send container metrics
let ContainerStats = setInterval(async () => {
let container_stats = await containerStats();
for (let i = 0; i < container_stats.length; i++) {
socket.emit('container_stats', container_stats[i]);
}
}, 1000);
// play/pause/stop/restart container
socket.on('clicked', (data) => {
if (clicked == true) { return; } clicked = true;
let buttonPress = {
user: socket.request.session.user,
role: socket.request.session.role,
action: data.action,
container: data.container,
state: data.state
}
containerAction(buttonPress);
clicked = false;
});
socket.on('disconnect', () => {
console.log('Stopping Metrics');
clearInterval(ServerMetrics);
console.log('Stopping Containers List');
clearInterval(DockerContainers);
socket.on('disconnect', () => {
clearInterval(ServerStats);
clearInterval(ContainerList);
clearInterval(ContainerStats);
});
});

View file

@ -12,6 +12,9 @@ function appCard(data) {
let command_check = command ? "checked" : "";
let privileged = data.privileged || "";
let privileged_check = privileged ? "checked" : "";
let repository = data.repository || "";
let source = data.image || "";
// if data.network is set to host, bridge, or docker set the radio button to checked
let net_host, net_bridge, net_docker = '';
@ -35,6 +38,11 @@ function appCard(data) {
}
if (repository != "") {
source = (`${repository.url}/raw/master/${repository.stackfile}`);
}
function CatagoryColor(category) {
switch (category) {
case 'Other':
@ -47,6 +55,8 @@ function appCard(data) {
return '<span class="badge bg-blue-lt">Dashboard</span> ';
case 'Communication':
return '<span class="badge bg-azure-lt">Communication</span> ';
case 'Media':
return '<span class="badge bg-azure-lt">Media</span> ';
case 'CMS':
return '<span class="badge bg-azure-lt">CMS</span> ';
case 'Monitoring':
@ -131,7 +141,11 @@ function appCard(data) {
let volume_check = volumes ? "checked" : "";
let volume_bind = volumes.bind.split(":")[0] ? volumes.bind.split(":")[0] : "";
let volume_container = volumes.container.split(":")[0] ? volumes.container.split(":")[0] : "";
let volume_readwrite = volumes.container.endsWith(":ro") ? "ro" : "rw";
let volume_readwrite = "rw"
if ((volumes.readonly == true) || (volumes.container.endsWith(":ro"))) {
volume_readwrite = "ro";
}
volumes_data.push({
check: volume_check,
@ -263,7 +277,7 @@ function appCard(data) {
</div>
<div class="col-lg-3">
<label class="form-label">Image: </label>
<input type="text" class="form-control" name="image" value="${data.image}"/>
<input type="text" class="form-control" name="image" value="${source}"/>
</div>
<div class="col-lg-3">
<label class="form-label">Restart Policy: </label>
@ -340,7 +354,7 @@ function appCard(data) {
<div class="row mb-1 align-items-end">
<div class="col-auto">
<input class="form-check-input" name="port_0_check" type="checkbox" ${ports_data[0].check}>
<input class="form-check-input" name="port0" type="checkbox" ${ports_data[0].check}>
</div>
<div class="col">
<label class="form-label">External Port</label>
@ -362,7 +376,7 @@ function appCard(data) {
<div class="row mb-1 align-items-end">
<div class="col-auto">
<input class="form-check-input" name="port_1_check" type="checkbox" ${ports_data[1].check}>
<input class="form-check-input" name="port1" type="checkbox" ${ports_data[1].check}>
</div>
<div class="col">
<input type="text" class="form-control" name="port_1_external" value="${ports_data[1].external}"/>
@ -381,7 +395,7 @@ function appCard(data) {
<div class="row mb-1 align-items-end">
<div class="col-auto">
<input class="form-check-input" name="port_2_check" type="checkbox" ${ports_data[2].check}>
<input class="form-check-input" name="port2" type="checkbox" ${ports_data[2].check}>
</div>
<div class="col">
<input type="text" class="form-control" name="port_2_external" value="${ports_data[2].external}"/>
@ -400,7 +414,7 @@ function appCard(data) {
<div class="row mb-1 align-items-end">
<div class="col-auto">
<input class="form-check-input" name="port_3_check" type="checkbox" ${ports_data[3].check}>
<input class="form-check-input" name="port3" type="checkbox" ${ports_data[3].check}>
</div>
<div class="col">
<input type="text" class="form-control" name="port_3_external" value="${ports_data[3].external}"/>
@ -419,7 +433,7 @@ function appCard(data) {
<div class="row mb-1 align-items-end">
<div class="col-auto">
<input class="form-check-input" name="port_4_check" type="checkbox" ${ports_data[4].check}>
<input class="form-check-input" name="port4" type="checkbox" ${ports_data[4].check}>
</div>
<div class="col">
<input type="text" class="form-control" name="port_4_external" value="${ports_data[4].external}"/>
@ -438,7 +452,7 @@ function appCard(data) {
<div class="row mb-1 align-items-end">
<div class="col-auto">
<input class="form-check-input" name="port_5_check" type="checkbox" ${ports_data[5].check}>
<input class="form-check-input" name="port5" type="checkbox" ${ports_data[5].check}>
</div>
<div class="col">
<input type="text" class="form-control" name="port_5_external" value="${ports_data[5].external}"/>
@ -471,7 +485,7 @@ function appCard(data) {
<div class="row mb-1 align-items-end">
<div class="col-auto">
<input class="form-check-input" name="volume_0_check" type="checkbox" ${volumes_data[0].check}>
<input class="form-check-input" name="volume0" type="checkbox" ${volumes_data[0].check}>
</div>
<div class="col">
<input type="text" class="form-control" name="volume_0_bind" value="${volumes_data[0].bind}"/>
@ -490,7 +504,7 @@ function appCard(data) {
<div class="row mb-1 align-items-end">
<div class="col-auto">
<input class="form-check-input" name="volume_1_check" type="checkbox" ${volumes_data[1].check}>
<input class="form-check-input" name="volume1" type="checkbox" ${volumes_data[1].check}>
</div>
<div class="col">
<input type="text" class="form-control" name="volume_1_bind" value="${volumes_data[1].bind}"/>
@ -509,7 +523,7 @@ function appCard(data) {
<div class="row mb-1 align-items-end">
<div class="col-auto">
<input class="form-check-input" name="volume_2_check" type="checkbox" ${volumes_data[2].check}>
<input class="form-check-input" name="volume2" type="checkbox" ${volumes_data[2].check}>
</div>
<div class="col">
<input type="text" class="form-control" name="volume_2_bind" value="${volumes_data[2].bind}"/>
@ -528,7 +542,7 @@ function appCard(data) {
<div class="row mb-1 align-items-end">
<div class="col-auto">
<input class="form-check-input" name="volume_3_check" type="checkbox" ${volumes_data[3].check}>
<input class="form-check-input" name="volume3" type="checkbox" ${volumes_data[3].check}>
</div>
<div class="col">
<input type="text" class="form-control" name="volume_3_bind" value="${volumes_data[3].bind}"/>
@ -547,7 +561,7 @@ function appCard(data) {
<div class="row mb-1 align-items-end">
<div class="col-auto">
<input class="form-check-input" name="volume_4_check" type="checkbox" ${volumes_data[4].check}>
<input class="form-check-input" name="volume4" type="checkbox" ${volumes_data[4].check}>
</div>
<div class="col">
<input type="text" class="form-control" name="volume_4_bind" value="${volumes_data[4].bind}"/>
@ -566,7 +580,7 @@ function appCard(data) {
<div class="row mb-1 align-items-end">
<div class="col-auto">
<input class="form-check-input" name="volume_5_check" type="checkbox" ${volumes_data[5].check}>
<input class="form-check-input" name="volume5" type="checkbox" ${volumes_data[5].check}>
</div>
<div class="col">
<input type="text" class="form-control" name="volume_5_bind" value="${volumes_data[5].bind}"/>
@ -599,7 +613,7 @@ function appCard(data) {
<div class="row mb-1 align-items-end">
<div class="col-auto">
<input class="form-check-input" type="checkbox" name="env_0_check" ${env_data[0].check}>
<input class="form-check-input" type="checkbox" name="env0" ${env_data[0].check}>
</div>
<div class="col">
<label class="form-label">Variable</label>
@ -613,7 +627,7 @@ function appCard(data) {
<div class="row mb-1 align-items-end">
<div class="col-auto">
<input class="form-check-input" type="checkbox" name="env_1_check" ${env_data[1].check}>
<input class="form-check-input" type="checkbox" name="env1" ${env_data[1].check}>
</div>
<div class="col">
<input type="text" class="form-control" name="env_1_name" value="${env_data[1].name}"/>
@ -625,7 +639,7 @@ function appCard(data) {
<div class="row mb-1 align-items-end">
<div class="col-auto">
<input class="form-check-input" type="checkbox" name="env_2_check" ${env_data[2].check}>
<input class="form-check-input" type="checkbox" name="env2" ${env_data[2].check}>
</div>
<div class="col">
<input type="text" class="form-control" name="env_2_name" value="${env_data[2].name}"/>
@ -637,7 +651,7 @@ function appCard(data) {
<div class="row mb-1 align-items-end">
<div class="col-auto">
<input class="form-check-input" type="checkbox" name="env_3_check" ${env_data[3].check}>
<input class="form-check-input" type="checkbox" name="env3" ${env_data[3].check}>
</div>
<div class="col">
<input type="text" class="form-control" name="env_3_name" value="${env_data[3].name}"/>
@ -649,7 +663,7 @@ function appCard(data) {
<div class="row mb-1 align-items-end">
<div class="col-auto">
<input class="form-check-input" type="checkbox" name="env_4_check" ${env_data[4].check}>
<input class="form-check-input" type="checkbox" name="env4" ${env_data[4].check}>
</div>
<div class="col">
<input type="text" class="form-control" name="env_4_name" value="${env_data[4].name}"/>
@ -661,7 +675,7 @@ function appCard(data) {
<div class="row mb-1 align-items-end">
<div class="col-auto">
<input class="form-check-input" type="checkbox" name="env_5_check" ${env_data[5].check}>
<input class="form-check-input" type="checkbox" name="env5" ${env_data[5].check}>
</div>
<div class="col">
<input type="text" class="form-control" name="env_5_name" value="${env_data[5].name}"/>
@ -673,7 +687,7 @@ function appCard(data) {
<div class="row mb-1 align-items-end">
<div class="col-auto">
<input class="form-check-input" type="checkbox" name="env_6_check" ${env_data[6].check}>
<input class="form-check-input" type="checkbox" name="env6" ${env_data[6].check}>
</div>
<div class="col">
<input type="text" class="form-control" name="env_6_name" value="${env_data[6].name}"/>
@ -686,7 +700,7 @@ function appCard(data) {
<div class="row mb-1 align-items-end">
<div class="col-auto">
<input class="form-check-input" type="checkbox" name="env_7_check" ${env_data[7].check}>
<input class="form-check-input" type="checkbox" name="env7" ${env_data[7].check}>
</div>
<div class="col">
<input type="text" class="form-control" name="env_7_name" value="${env_data[7].name}"/>
@ -699,7 +713,7 @@ function appCard(data) {
<div class="row mb-1 align-items-end">
<div class="col-auto">
<input class="form-check-input" type="checkbox" name="env_8_check" ${env_data[8].check}>
<input class="form-check-input" type="checkbox" name="env8" ${env_data[8].check}>
</div>
<div class="col">
<input type="text" class="form-control" name="env_8_name" value="${env_data[8].name}"/>
@ -712,7 +726,7 @@ function appCard(data) {
<div class="row mb-1 align-items-end">
<div class="col-auto">
<input class="form-check-input" type="checkbox" name="env_9_check" ${env_data[9].check}>
<input class="form-check-input" type="checkbox" name="env9" ${env_data[9].check}>
</div>
<div class="col">
<input type="text" class="form-control" name="env_9_name" value="${env_data[9].name}"/>
@ -725,7 +739,7 @@ function appCard(data) {
<div class="row mb-1 align-items-end">
<div class="col-auto">
<input class="form-check-input" type="checkbox" name="env_10_check" ${env_data[10].check}>
<input class="form-check-input" type="checkbox" name="env10" ${env_data[10].check}>
</div>
<div class="col">
<input type="text" class="form-control" name="env_10_name" value="${env_data[10].name}"/>
@ -738,7 +752,7 @@ function appCard(data) {
<div class="row mb-1 align-items-end">
<div class="col-auto">
<input class="form-check-input" type="checkbox" name="env_11_check" ${env_data[11].check}>
<input class="form-check-input" type="checkbox" name="env11" ${env_data[11].check}>
</div>
<div class="col">
<input type="text" class="form-control" name="env_11_name" value="${env_data[11].name}"/>
@ -766,7 +780,7 @@ function appCard(data) {
<div class="row mb-1 align-items-end">
<div class="col-auto">
<input class="form-check-input" type="checkbox" name="label_0_check" ${label_data[0].check}>
<input class="form-check-input" type="checkbox" name="label0" ${label_data[0].check}>
</div>
<div class="col">
<label class="form-label">Variable</label>
@ -780,7 +794,7 @@ function appCard(data) {
<div class="row mb-1 align-items-end">
<div class="col-auto">
<input class="form-check-input" type="checkbox" name="label_1_check" ${label_data[1].check}>
<input class="form-check-input" type="checkbox" name="label1" ${label_data[1].check}>
</div>
<div class="col">
<input type="text" class="form-control" name="label_1_name" value="${label_data[1].name}"/>
@ -793,7 +807,7 @@ function appCard(data) {
<div class="row mb-1 align-items-end">
<div class="col-auto">
<input class="form-check-input" type="checkbox" name="label_2_check" ${label_data[2].check}>
<input class="form-check-input" type="checkbox" name="label2" ${label_data[2].check}>
</div>
<div class="col">
<input type="text" class="form-control" name="label_2_name" value="${label_data[2].name}"/>
@ -805,7 +819,7 @@ function appCard(data) {
<div class="row mb-1 align-items-end">
<div class="col-auto">
<input class="form-check-input" type="checkbox" name="label_3_check" ${label_data[3].check}>
<input class="form-check-input" type="checkbox" name="label3" ${label_data[3].check}>
</div>
<div class="col">
<input type="text" class="form-control" name="label_3_name" value="${label_data[3].name}"/>
@ -817,7 +831,7 @@ function appCard(data) {
<div class="row mb-1 align-items-end">
<div class="col-auto">
<input class="form-check-input" type="checkbox" name="label_4_check" ${label_data[4].check}>
<input class="form-check-input" type="checkbox" name="label4" ${label_data[4].check}>
</div>
<div class="col">
<input type="text" class="form-control" name="label_4_name" value="${label_data[4].name}"/>
@ -829,7 +843,7 @@ function appCard(data) {
<div class="row mb-1 align-items-end">
<div class="col-auto">
<input class="form-check-input" type="checkbox" name="label_5_check" ${label_data[5].check}>
<input class="form-check-input" type="checkbox" name="label5" ${label_data[5].check}>
</div>
<div class="col">
<input type="text" class="form-control" name="label_5_name" value="${label_data[5].name}"/>
@ -841,7 +855,7 @@ function appCard(data) {
<div class="row mb-1 align-items-end">
<div class="col-auto">
<input class="form-check-input" type="checkbox" name="label_6_check" ${label_data[6].check}>
<input class="form-check-input" type="checkbox" name="label6" ${label_data[6].check}>
</div>
<div class="col">
<input type="text" class="form-control" name="label_6_name" value="${label_data[6].name}"/>
@ -853,7 +867,7 @@ function appCard(data) {
<div class="row mb-1 align-items-end">
<div class="col-auto">
<input class="form-check-input" type="checkbox" name="label_7_check" ${label_data[7].check}>
<input class="form-check-input" type="checkbox" name="label7" ${label_data[7].check}>
</div>
<div class="col">
<input type="text" class="form-control" name="label_7_name" value="${label_data[7].name}"/>
@ -865,7 +879,7 @@ function appCard(data) {
<div class="row mb-1 align-items-end">
<div class="col-auto">
<input class="form-check-input" type="checkbox" name="label_8_check" ${label_data[8].check}>
<input class="form-check-input" type="checkbox" name="label8" ${label_data[8].check}>
</div>
<div class="col">
<input type="text" class="form-control" name="label_8_name" value="${label_data[8].name}"/>
@ -877,7 +891,7 @@ function appCard(data) {
<div class="row mb-1 align-items-end">
<div class="col-auto">
<input class="form-check-input" type="checkbox" name="label_9_check" ${label_data[9].check}>
<input class="form-check-input" type="checkbox" name="label9" ${label_data[9].check}>
</div>
<div class="col">
<input type="text" class="form-control" name="label_9_name" value="${label_data[9].name}"/>
@ -889,7 +903,7 @@ function appCard(data) {
<div class="row mb-1 align-items-end">
<div class="col-auto">
<input class="form-check-input" type="checkbox" name="label_10_check" ${label_data[10].check}>
<input class="form-check-input" type="checkbox" name="label10" ${label_data[10].check}>
</div>
<div class="col">
<input type="text" class="form-control" name="label_10_name" value="${label_data[10].name}"/>
@ -901,7 +915,7 @@ function appCard(data) {
<div class="row mb-1 align-items-end">
<div class="col-auto">
<input class="form-check-input" type="checkbox" name="label_11_check" ${label_data[11].check}>
<input class="form-check-input" type="checkbox" name="label11" ${label_data[11].check}>
</div>
<div class="col">
<input type="text" class="form-control" name="label_11_name" value="${label_data[11].name}"/>

View file

@ -1,4 +1,6 @@
function dashCard(name, service, id, state, image, external_port, internal_port) {
module.exports.dashCard = function dashCard(data) {
let { name, service, id, state, image, external_port, internal_port } = data;
//disable controls for a docker container depending on its name
let enabled = "";
@ -100,7 +102,7 @@ function dashCard(name, service, id, state, image, external_port, internal_port)
<div class="card">
<div class="card-body">
<div class="card-stamp card-stamp-sm">
<img heigh="300px" width="300px" src="https://raw.githubusercontent.com/walkxcode/dashboard-icons/main/png/${service}.png"></img>
<img heigh="150px" width="150px" src="https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/${service}.png"></img>
</div>
<div class="d-flex align-items-center">
@ -150,7 +152,7 @@ function dashCard(name, service, id, state, image, external_port, internal_port)
</span>
</div>
</div>
<div id="cardChart" class="chart-sm"></div>
<div id="${name}_chart" class="chart-sm"></div>
</div>
</div>
</div>
@ -1062,6 +1064,4 @@ function dashCard(name, service, id, state, image, external_port, internal_port)
</div>
</div>
</div>`;
}
module.exports = { dashCard };
}

View file

@ -1,6 +1,7 @@
const { writeFileSync, mkdirSync, appendFileSync } = require("fs");
const { exec } = require("child_process");
const { writeFileSync, mkdirSync, readFileSync } = require("fs");
const { exec, execSync } = require("child_process");
const { dashCard } = require('../components/dashCard');
const yaml = require('js-yaml');
@ -8,122 +9,147 @@ exports.Install = async function (req, res) {
if (req.session.role == "admin") {
console.log(req.body);
let { service_name, name, image, command_check, command, net_mode, restart_policy } = req.body;
let { port_0_check, port_1_check, port_2_check, port_3_check, port_4_check, port_5_check } = req.body;
let { volume_0_check, volume_1_check, volume_2_check, volume_3_check, volume_4_check, volume_5_check } = req.body;
let { env_0_check, env_1_check, env_2_check, env_3_check, env_4_check, env_5_check, env_6_check, env_7_check, env_8_check, env_9_check, env_10_check, env_11_check } = req.body;
let { label_0_check, label_1_check, label_2_check, label_3_check, label_4_check, label_5_check, label_6_check, label_7_check, label_8_check, label_9_check, label_10_check, label_11_check } = req.body;
let { port0, port1, port2, port3, port4, port5 } = req.body;
let { volume0, volume1, volume2, volume3, volume4, volume5 } = req.body;
let { env0, env1, env2, env3, env4, env5, env6, env7, env8, env9, env10, env11 } = req.body;
let { label0, label1, label2, label3, label4, label5, label6, label7, label8, label9, label10, label11 } = req.body;
let installCard = dashCard(req.body.name, req.body.service_name, '', 'installing', req.body.image, 0, 0);
req.app.locals.install = installCard;
if (image.startsWith('https://')){
mkdirSync(`./appdata/${name}`, { recursive: true });
execSync(`curl -o ./appdata/${name}/${name}_stack.yml -L ${image}`);
console.log(`Downloaded stackfile: ${image}`);
let stackfile = yaml.load(readFileSync(`./appdata/${name}/${name}_stack.yml`, 'utf8'));
let services = Object.keys(stackfile.services);
let compose_file = `version: '3'`;
compose_file += `\nservices:`
compose_file += `\n ${service_name}:`
compose_file += `\n container_name: ${name}`;
compose_file += `\n image: ${image}`;
for ( let i = 0; i < services.length; i++ ) {
try {
console.log(stackfile.services[Object.keys(stackfile.services)[i]].environment);
} catch { console.log('no env') }
}
} else {
// Command
if (command_check == 'on') {
compose_file += `\n command: ${command}`
}
let container_info = {
name: name,
service: service_name,
state: 'installing',
image: image,
restart_policy: restart_policy
}
// Network mode
if (net_mode == 'host') {
compose_file += `\n network_mode: 'host'`
}
else if (net_mode != 'host' && net_mode != 'docker') {
compose_file += `\n network_mode: '${net_mode}'`
}
// Restart policy
if (restart_policy != '') {
compose_file += `\n restart: ${restart_policy}`
}
let installCard = dashCard(container_info);
// Ports
if ((port_0_check == 'on' || port_1_check == 'on' || port_2_check == 'on' || port_3_check == 'on' || port_4_check == 'on' || port_5_check == 'on') && (net_mode != 'host')) {
compose_file += `\n ports:`
req.app.locals.install = installCard;
let compose_file = `version: '3'`;
compose_file += `\nservices:`
compose_file += `\n ${service_name}:`
compose_file += `\n container_name: ${name}`;
compose_file += `\n image: ${image}`;
// Command
if (command_check == 'on') {
compose_file += `\n command: ${command}`
}
// Network mode
if (net_mode == 'host') {
compose_file += `\n network_mode: 'host'`
}
else if (net_mode != 'host' && net_mode != 'docker') {
compose_file += `\n network_mode: '${net_mode}'`
}
// Restart policy
if (restart_policy != '') {
compose_file += `\n restart: ${restart_policy}`
}
// Ports
if ((port0 == 'on' || port1 == 'on' || port2 == 'on' || port3 == 'on' || port4 == 'on' || port5 == 'on') && (net_mode != 'host')) {
compose_file += `\n ports:`
for (let i = 0; i < 6; i++) {
if (req.body[`port${i}`] == 'on') {
compose_file += `\n - ${req.body[`port_${i}_external`]}:${req.body[`port_${i}_internal`]}/${req.body[`port_${i}_protocol`]}`
}
}
}
// Volumes
if (volume0 == 'on' || volume1 == 'on' || volume2 == 'on' || volume3 == 'on' || volume4 == 'on' || volume5 == 'on') {
compose_file += `\n volumes:`
for (let i = 0; i < 6; i++) {
if (req.body[`port_${i}_check`] == 'on') {
compose_file += `\n - ${req.body[`port_${i}_external`]}:${req.body[`port_${i}_internal`]}/${req.body[`port_${i}_protocol`]}`
if (req.body[`volume${i}`] == 'on') {
compose_file += `\n - ${req.body[`volume_${i}_bind`]}:${req.body[`volume_${i}_container`]}:${req.body[`volume_${i}_readwrite`]}`
}
}
}
// Volumes
if (volume_0_check == 'on' || volume_1_check == 'on' || volume_2_check == 'on' || volume_3_check == 'on' || volume_4_check == 'on' || volume_5_check == 'on') {
compose_file += `\n volumes:`
for (let i = 0; i < 6; i++) {
if (req.body[`volume_${i}_check`] == 'on') {
compose_file += `\n - ${req.body[`volume_${i}_bind`]}:${req.body[`volume_${i}_container`]}:${req.body[`volume_${i}_readwrite`]}`
}
}
}
// Environment variables
if (env_0_check == 'on' || env_1_check == 'on' || env_2_check == 'on' || env_3_check == 'on' || env_4_check == 'on' || env_5_check == 'on' || env_6_check == 'on' || env_7_check == 'on' || env_8_check == 'on' || env_9_check == 'on' || env_10_check == 'on' || env_11_check == 'on') {
compose_file += `\n environment:`
}
for (let i = 0; i < 12; i++) {
if (req.body[`env_${i}_check`] == 'on') {
compose_file += `\n - ${req.body[`env_${i}_name`]}=${req.body[`env_${i}_default`]}`
// Environment variables
if (env0 == 'on' || env1 == 'on' || env2 == 'on' || env3 == 'on' || env4 == 'on' || env5 == 'on' || env6 == 'on' || env7 == 'on' || env8 == 'on' || env9 == 'on' || env10 == 'on' || env11 == 'on') {
compose_file += `\n environment:`
}
}
// Add labels
if (label_0_check == 'on' || label_1_check == 'on' || label_2_check == 'on' || label_3_check == 'on' || label_4_check == 'on' || label_5_check == 'on' || label_6_check == 'on' || label_7_check == 'on' || label_8_check == 'on' || label_9_check == 'on' || label_10_check == 'on' || label_11_check == 'on') {
compose_file += `\n labels:`
}
for (let i = 0; i < 12; i++) {
if (req.body[`label_${i}_check`] == 'on') {
compose_file += `\n - ${req.body[`label_${i}_name`]}=${req.body[`label_${i}_value`]}`
}
}
// Add privileged mode
if (req.body.privileged == 'on') {
compose_file += `\n privileged: true`
}
// Add hardware acceleration to the docker-compose file if one of the environment variables has the label DRINODE
if (env_0_check == 'on' || env_1_check == 'on' || env_2_check == 'on' || env_3_check == 'on' || env_4_check == 'on' || env_5_check == 'on' || env_6_check == 'on' || env_7_check == 'on' || env_8_check == 'on' || env_9_check == 'on' || env_10_check == 'on' || env_11_check == 'on') {
for (let i = 0; i < 12; i++) {
if (req.body[`env_${i}_check`] == 'on') {
if (req.body[`env_${i}_name`] == 'DRINODE') {
compose_file += `\n deploy:`
compose_file += `\n resources:`
compose_file += `\n reservations:`
compose_file += `\n devices:`
compose_file += `\n - driver: nvidia`
compose_file += `\n count: 1`
compose_file += `\n capabilities: [gpu]`
if (req.body[`env${i}`] == 'on') {
compose_file += `\n - ${req.body[`env_${i}_name`]}=${req.body[`env_${i}_default`]}`
}
}
// Add labels
if (label0 == 'on' || label1 == 'on' || label2 == 'on' || label3 == 'on' || label4 == 'on' || label5 == 'on' || label6 == 'on' || label7 == 'on' || label8 == 'on' || label9 == 'on' || label10 == 'on' || label11 == 'on') {
compose_file += `\n labels:`
}
for (let i = 0; i < 12; i++) {
if (req.body[`label${i}`] == 'on') {
compose_file += `\n - ${req.body[`label_${i}_name`]}=${req.body[`label_${i}_value`]}`
}
}
// Add privileged mode
if (req.body.privileged == 'on') {
compose_file += `\n privileged: true`
}
// Add hardware acceleration to the docker-compose file if one of the environment variables has the label DRINODE
if (env0 == 'on' || env1 == 'on' || env2 == 'on' || env3 == 'on' || env4 == 'on' || env5 == 'on' || env6 == 'on' || env7 == 'on' || env8 == 'on' || env9 == 'on' || env10 == 'on' || env11 == 'on') {
for (let i = 0; i < 12; i++) {
if (req.body[`env${i}`] == 'on') {
if (req.body[`env_${i}_name`] == 'DRINODE') {
compose_file += `\n deploy:`
compose_file += `\n resources:`
compose_file += `\n reservations:`
compose_file += `\n devices:`
compose_file += `\n - driver: nvidia`
compose_file += `\n count: 1`
compose_file += `\n capabilities: [gpu]`
}
}
}
}
try {
mkdirSync(`./appdata/${name}`, { recursive: true });
writeFileSync(`./appdata/${name}/docker-compose.yml`, compose_file, function (err) { console.log(err) });
exec(`docker compose -f ./appdata/${name}/docker-compose.yml up -d`, (error, stdout, stderr) => {
if (error) { console.error(`error: ${error.message}`); return; }
if (stderr) { console.error(`stderr: ${stderr}`); return; }
console.log(`stdout:\n${stdout}`);
});
} catch { console.log('error creating directory or compose file') }
}
try {
mkdirSync(`./appdata/${name}`, { recursive: true });
writeFileSync(`./appdata/${name}/docker-compose.yml`, compose_file, function (err) { console.log(err) });
exec(`docker compose -f ./appdata/${name}/docker-compose.yml up -d`, (error, stdout, stderr) => {
if (error) { console.error(`error: ${error.message}`); return; }
if (stderr) { console.error(`stderr: ${stderr}`); return; }
console.log(`stdout:\n${stdout}`);
});
} catch { console.log('error creating directory or compose file') }
// Redirect to the home page
res.redirect("/");
} else {

View file

@ -1,5 +1,12 @@
const User = require('../database/UserModel');
const { appCard } = require('../components/appCard')
const { exec, execSync } = require("child_process");
const { dashCard } = require('../components/dashCard');
const yaml = require('js-yaml');
const { install } = require('../functions/package_manager');
const templates_json = require('../templates.json');
let templates = templates_json.templates;
@ -35,6 +42,7 @@ exports.Apps = async function(req, res) {
let apps_list = '';
for (let i = list_start; i < list_end && i < templates.length; i++) {
let app_card = appCard(templates[i]);
apps_list += app_card;
}
@ -59,7 +67,7 @@ exports.Apps = async function(req, res) {
exports.processApps = async function(req, res) {
exports.searchApps = async function(req, res) {
if (req.session.role == "admin") {
// Get the user.
@ -126,4 +134,63 @@ exports.processApps = async function(req, res) {
// Redirect to the login page
res.redirect("/login");
}
}
exports.Install = async function (req, res) {
if (req.session.role == "admin") {
install(req.body);
let container_info = {
name: req.body.name,
service: req.body.service_name,
state: 'installing',
image: req.body.image,
restart_policy: req.body.restart_policy
}
let installCard = dashCard(container_info);
req.app.locals.install = installCard;
// Redirect to the home page
res.redirect("/");
} else {
// Redirect to the login page
res.redirect("/login");
}
}
exports.Uninstall = async function (req, res) {
if (req.session.role == "admin") {
if (req.body.confirm == 'Yes') {
exec(`docker compose -f ./appdata/${req.body.service_name}/docker-compose.yml down`, (error, stdout, stderr) => {
if (error) { console.error(`error: ${error.message}`); return; }
if (stderr) { console.error(`stderr: ${stderr}`); return; }
console.log(`stdout:\n${stdout}`);
});
}
// Redirect to the home page
res.redirect("/");
} else {
// Redirect to the login page
res.redirect("/login");
}
}

View file

@ -1,4 +1,5 @@
const User = require('../database/UserModel.js');
const Server = require('../database/ServerSettings.js');
exports.Settings = async function(req, res) {
if (req.session.role == "admin") {

View file

@ -0,0 +1,42 @@
const { Sequelize, DataTypes } = require('sequelize');
const sequelize = new Sequelize({
dialect: 'sqlite',
storage: 'database/db.sqlite',
logging: false
});
const Server = sequelize.define('Server', {
// Model attributes are defined here
timezone: {
type: DataTypes.STRING,
allowNull: false
},
hwa: {
type: DataTypes.STRING
// allowNull defaults to true
},
media: {
type: DataTypes.STRING
// allowNull defaults to true
},
pgid: {
type: DataTypes.STRING
// allowNull defaults to true
},
puid: {
type: DataTypes.STRING
// allowNull defaults to true
}
});
async function syncModel() {
await sequelize.sync();
console.log('Server model synced');
}
syncModel();
module.exports = Server;

View file

@ -0,0 +1,161 @@
const { writeFileSync, mkdirSync, readFileSync } = require("fs");
const { exec, execSync } = require("child_process");
const { dashCard } = require('../components/dashCard');
const yaml = require('js-yaml');
module.exports.install = async function (data) {
let { service_name, name, image, command_check, command, net_mode, restart_policy } = data;
let { port0, port1, port2, port3, port4, port5 } = data;
let { volume0, volume1, volume2, volume3, volume4, volume5 } = data;
let { env0, env1, env2, env3, env4, env5, env6, env7, env8, env9, env10, env11 } = data;
let { label0, label1, label2, label3, label4, label5, label6, label7, label8, label9, label10, label11 } = data;
if (image.startsWith('https://')){
mkdirSync(`./appdata/${name}`, { recursive: true });
execSync(`curl -o ./appdata/${name}/${name}_stack.yml -L ${image}`);
console.log(`Downloaded stackfile: ${image}`);
let stackfile = yaml.load(readFileSync(`./appdata/${name}/${name}_stack.yml`, 'utf8'));
let services = Object.keys(stackfile.services);
for ( let i = 0; i < services.length; i++ ) {
try {
console.log(stackfile.services[Object.keys(stackfile.services)[i]].environment);
} catch { console.log('no env') }
}
} else {
let compose_file = `version: '3'`;
compose_file += `\nservices:`
compose_file += `\n ${service_name}:`
compose_file += `\n container_name: ${name}`;
compose_file += `\n image: ${image}`;
// Command
if (command_check == 'on') {
compose_file += `\n command: ${command}`
}
// Network mode
if (net_mode == 'host') {
compose_file += `\n network_mode: 'host'`
}
else if (net_mode != 'host' && net_mode != 'docker') {
compose_file += `\n network_mode: '${net_mode}'`
}
// Restart policy
if (restart_policy != '') {
compose_file += `\n restart: ${restart_policy}`
}
// Ports
if ((port0 == 'on' || port1 == 'on' || port2 == 'on' || port3 == 'on' || port4 == 'on' || port5 == 'on') && (net_mode != 'host')) {
compose_file += `\n ports:`
for (let i = 0; i < 6; i++) {
if (data[`port${i}`] == 'on') {
compose_file += `\n - ${data[`port_${i}_external`]}:${data[`port_${i}_internal`]}/${data[`port_${i}_protocol`]}`
}
}
}
// Volumes
if (volume0 == 'on' || volume1 == 'on' || volume2 == 'on' || volume3 == 'on' || volume4 == 'on' || volume5 == 'on') {
compose_file += `\n volumes:`
for (let i = 0; i < 6; i++) {
if (data[`volume${i}`] == 'on') {
compose_file += `\n - ${data[`volume_${i}_bind`]}:${data[`volume_${i}_container`]}:${data[`volume_${i}_readwrite`]}`
}
}
}
// Environment variables
if (env0 == 'on' || env1 == 'on' || env2 == 'on' || env3 == 'on' || env4 == 'on' || env5 == 'on' || env6 == 'on' || env7 == 'on' || env8 == 'on' || env9 == 'on' || env10 == 'on' || env11 == 'on') {
compose_file += `\n environment:`
}
for (let i = 0; i < 12; i++) {
if (data[`env${i}`] == 'on') {
compose_file += `\n - ${data[`env_${i}_name`]}=${data[`env_${i}_default`]}`
}
}
// Add labels
if (label0 == 'on' || label1 == 'on' || label2 == 'on' || label3 == 'on' || label4 == 'on' || label5 == 'on' || label6 == 'on' || label7 == 'on' || label8 == 'on' || label9 == 'on' || label10 == 'on' || label11 == 'on') {
compose_file += `\n labels:`
}
for (let i = 0; i < 12; i++) {
if (data[`label${i}`] == 'on') {
compose_file += `\n - ${data[`label_${i}_name`]}=${data[`label_${i}_value`]}`
}
}
// Add privileged mode
if (data.privileged == 'on') {
compose_file += `\n privileged: true`
}
// Add hardware acceleration to the docker-compose file if one of the environment variables has the label DRINODE
if (env0 == 'on' || env1 == 'on' || env2 == 'on' || env3 == 'on' || env4 == 'on' || env5 == 'on' || env6 == 'on' || env7 == 'on' || env8 == 'on' || env9 == 'on' || env10 == 'on' || env11 == 'on') {
for (let i = 0; i < 12; i++) {
if (data[`env${i}`] == 'on') {
if (data[`env_${i}_name`] == 'DRINODE') {
compose_file += `\n deploy:`
compose_file += `\n resources:`
compose_file += `\n reservations:`
compose_file += `\n devices:`
compose_file += `\n - driver: nvidia`
compose_file += `\n count: 1`
compose_file += `\n capabilities: [gpu]`
}
}
}
}
try {
mkdirSync(`./appdata/${name}`, { recursive: true });
writeFileSync(`./appdata/${name}/docker-compose.yml`, compose_file, function (err) { console.log(err) });
exec(`docker compose -f ./appdata/${name}/docker-compose.yml up -d`, (error, stdout, stderr) => {
if (error) { console.error(`error: ${error.message}`); return; }
if (stderr) { console.error(`stderr: ${stderr}`); return; }
console.log(`stdout:\n${stdout}`);
});
} catch { console.log('error creating directory or compose file') }
}
}
module.exports.uninstall = async function (data) {
if (req.session.role == "admin") {
if (data.confirm == 'Yes') {
exec(`docker compose -f ./appdata/${data.service_name}/docker-compose.yml down`, (error, stdout, stderr) => {
if (error) { console.error(`error: ${error.message}`); return; }
if (stderr) { console.error(`stderr: ${stderr}`); return; }
console.log(`stdout:\n${stdout}`);
});
}
// Redirect to the home page
res.redirect("/");
} else {
// Redirect to the login page
res.redirect("/login");
}
}

View file

@ -0,0 +1,150 @@
const { currentLoad, mem, networkStats, fsSize, dockerContainerStats } = require('systeminformation');
var Docker = require('dockerode');
var docker = new Docker({ socketPath: '/var/run/docker.sock' });
const { dashCard } = require('../components/dashCard');
module.exports.serverStats = async function () {
const cpuUsage = await currentLoad();
const ramUsage = await mem();
const netUsage = await networkStats();
const diskUsage = await fsSize();
const info = {
cpu: Math.round(cpuUsage.currentLoad),
ram: Math.round((ramUsage.active / ramUsage.total) * 100),
tx: netUsage[0].tx_bytes,
rx: netUsage[0].rx_bytes,
disk: diskUsage[0].use,
};
return info;
}
module.exports.containerList = async function () {
let card_list = '';
const data = await docker.listContainers({ all: true });
for (const container of data) {
let imageVersion = container.Image.split('/');
let service = imageVersion[imageVersion.length - 1].split(':')[0];
let containerId = docker.getContainer(container.Id);
let containerInfo = await containerId.inspect();
let open_ports = [];
let external_port = 0;
let internal_port = 0;
for (const [key, value] of Object.entries(containerInfo.HostConfig.PortBindings)) {
open_ports.push(`${value[0].HostPort}`);
external_port = value[0].HostPort;
internal_port = key;
if ((external_port == undefined) || (internal_port == undefined)) {
external_port = 0;
internal_port = 0;
}
}
let volumes = [];
for (const [key, value] of Object.entries(containerInfo.Mounts)) {
volumes.push(`${value.Source}: ${value.Destination}: ${value.RW}`);
}
let environment_variables = [];
for (const [key, value] of Object.entries(containerInfo.Config.Env)) {
environment_variables.push(`${key}: ${value}`);
}
let labels = [];
for (const [key, value] of Object.entries(containerInfo.Config.Labels)) {
labels.push(`${key}: ${value}`);
}
let container_info = {
name: container.Names[0].slice(1),
service: service,
id: container.Id,
state: container.State,
image: container.Image,
external_port: external_port,
internal_port: internal_port
}
let dockerCard = dashCard(container_info);
card_list += dockerCard;
}
return card_list;
}
module.exports.containerStats = async function () {
let container_stats = [];
const data = await docker.listContainers({ all: true });
for (const container of data) {
const stats = await dockerContainerStats(container.Id);
let container_stat = {
name: container.Names[0].slice(1),
cpu: Math.round(stats[0].cpuPercent),
ram: Math.round(stats[0].memPercent)
}
//push stats to an array
container_stats.push(container_stat);
}
return container_stats;
}
module.exports.containerAction = async function (data) {
let { user, role, action, container, state } = data;
console.log(`${user} wants to: ${action} ${container}`);
if (role == 'admin') {
var containerName = docker.getContainer(container);
if ((action == 'start') && (state == 'stopped')) {
containerName.start();
} else if ((action == 'start') && (state == 'paused')) {
containerName.unpause();
} else if ((action == 'stop') && (state != 'stopped')) {
containerName.stop();
} else if ((action == 'pause') && (state == 'running')) {
containerName.pause();
} else if ((action == 'pause') && (state == 'paused')) {
containerName.unpause();
} else if (action == 'restart') {
containerName.restart();
}
} else {
console.log('User is not an admin');
}
}

View file

@ -0,0 +1,150 @@
const { currentLoad, mem, networkStats, fsSize, dockerContainerStats } = require('systeminformation');
var Docker = require('dockerode');
var docker = new Docker({ socketPath: '/var/run/docker.sock' });
const { dashCard } = require('../components/dashCard');
module.exports.serverStats = async function () {
const cpuUsage = await currentLoad();
const ramUsage = await mem();
const netUsage = await networkStats();
const diskUsage = await fsSize();
const info = {
cpu: Math.round(cpuUsage.currentLoad),
ram: Math.round((ramUsage.active / ramUsage.total) * 100),
tx: netUsage[0].tx_bytes,
rx: netUsage[0].rx_bytes,
disk: diskUsage[0].use,
};
return info;
}
module.exports.containerList = async function () {
let card_list = '';
const data = await docker.listContainers({ all: true });
for (const container of data) {
let imageVersion = container.Image.split('/');
let service = imageVersion[imageVersion.length - 1].split(':')[0];
let containerId = docker.getContainer(container.Id);
let containerInfo = await containerId.inspect();
let open_ports = [];
let external_port = 0;
let internal_port = 0;
for (const [key, value] of Object.entries(containerInfo.HostConfig.PortBindings)) {
open_ports.push(`${value[0].HostPort}`);
external_port = value[0].HostPort;
internal_port = key;
if ((external_port == undefined) || (internal_port == undefined)) {
external_port = 0;
internal_port = 0;
}
}
let volumes = [];
for (const [key, value] of Object.entries(containerInfo.Mounts)) {
volumes.push(`${value.Source}: ${value.Destination}: ${value.RW}`);
}
let environment_variables = [];
for (const [key, value] of Object.entries(containerInfo.Config.Env)) {
environment_variables.push(`${key}: ${value}`);
}
let labels = [];
for (const [key, value] of Object.entries(containerInfo.Config.Labels)) {
labels.push(`${key}: ${value}`);
}
let container_info = {
name: container.Names[0].slice(1),
service: service,
id: container.Id,
state: container.State,
image: container.Image,
external_port: external_port,
internal_port: internal_port
}
let dockerCard = dashCard(container_info);
card_list += dockerCard;
}
return card_list;
}
module.exports.containerStats = async function () {
let container_stats = [];
const data = await docker.listContainers({ all: true });
for (const container of data) {
const stats = await dockerContainerStats(container.Id);
let container_stat = {
name: container.Names[0].slice(1),
cpu: Math.round(stats[0].cpuPercent),
ram: Math.round(stats[0].memPercent)
}
//push stats to an array
container_stats.push(container_stat);
}
return container_stats;
}
module.exports.containerAction = async function (data) {
let { user, role, action, container, state } = data;
console.log(`${user} wants to: ${action} ${container}`);
if (role == 'admin') {
var containerName = docker.getContainer(container);
if ((action == 'start') && (state == 'stopped')) {
containerName.start();
} else if ((action == 'start') && (state == 'paused')) {
containerName.unpause();
} else if ((action == 'stop') && (state != 'stopped')) {
containerName.stop();
} else if ((action == 'pause') && (state == 'running')) {
containerName.pause();
} else if ((action == 'pause') && (state == 'paused')) {
containerName.unpause();
} else if (action == 'restart') {
containerName.restart();
}
} else {
console.log('User is not an admin');
}
}

View file

@ -17,7 +17,8 @@
"sequelize": "^6.32.1",
"socket.io": "^4.6.1",
"sqlite3": "^5.1.6",
"systeminformation": "^5.17.12"
"systeminformation": "^5.17.12",
"js-yaml": "^4.1.0"
},
"description": ""
}

View file

@ -22,8 +22,13 @@ const diskBar = document.getElementById('disk-bar');
const dockerCards = document.getElementById('cards');
// create
//Update usage bars
socket.on('metrics', ({ cpu, ram, tx, rx, disk}) => {
socket.on('metrics', (data) => {
let {cpu, ram, tx, rx, disk} = data;
cpuText.innerHTML = `<span>CPU ${cpu} %</span>`;
cpuBar.innerHTML = `<span style="width: ${cpu}%"><span></span></span>`;
ramText.innerHTML = `<span>RAM ${ram} %</span>`;
@ -32,8 +37,17 @@ socket.on('metrics', ({ cpu, ram, tx, rx, disk}) => {
diskBar.innerHTML = `<span style="width: ${disk}%"><span></span></span>`;
});
function drawCharts() {
var elements = document.querySelectorAll("#cardChart");
function drawCharts(name, cpu_array, ram_array) {
var elements = document.querySelectorAll(`${name}`);
if (cpu_array == undefined) {
cpu_array = [37, 35, 44, 28, 36, 24, 65, 31, 37, 39, 62, 51, 35, 41, 35, 27, 93, 53, 61, 27, 54, 43, 4, 46, 39, 62, 51, 35, 41, 67];
}
if (ram_array == undefined) {
ram_array = [93, 54, 51, 24, 35, 35, 31, 67, 19, 43, 28, 36, 62, 61, 27, 39, 35, 41, 27, 35, 51, 46, 62, 37, 44, 53, 41, 65, 39, 37];
}
Array.from(elements).forEach(function(element) {
if (window.ApexCharts) {
@ -60,10 +74,10 @@ function drawCharts() {
},
series: [{
name: "CPU",
data: [37, 35, 44, 28, 36, 24, 65, 31, 37, 39, 62, 51, 35, 41, 35, 27, 93, 53, 61, 27, 54, 43, 4, 46, 39, 62, 51, 35, 41, 67]
data: cpu_array
}, {
name: "RAM",
data: [93, 54, 51, 24, 35, 35, 31, 67, 19, 43, 28, 36, 62, 61, 27, 39, 35, 41, 27, 35, 51, 46, 62, 37, 44, 53, 41, 65, 39, 37]
data: ram_array
}],
tooltip: {
theme: 'dark'
@ -97,14 +111,8 @@ function drawCharts() {
});
}
// container button actions
function buttonAction(button) {
// if the button name is 'CaddyProxyManager' and the value is 'install' grab the div with the id of 'sites' and remove d-none class. also add the d-none class to the div with the id of 'CaddyInstallCard'
if (button.name == 'CaddyProxyManager' && button.value == 'install') {
document.getElementById('sites').classList.remove('d-none');
document.getElementById('CaddyInstallCard').classList.add('d-none');
}
socket.emit('clicked', {container: button.name, state: button.id, action: button.value});
}
@ -117,10 +125,45 @@ socket.on('cards', (data) => {
});
dockerCards.insertAdjacentHTML("afterend", data);
drawCharts();
});
socket.on('container_stats', (data) => {
let {name, cpu, ram} = data;
// get cpu and ram array of the container from local storage
var cpu_array = JSON.parse(localStorage.getItem(`${name}_cpu`));
var ram_array = JSON.parse(localStorage.getItem(`${name}_ram`));
console.log(`#1: ${name} cpu: ${cpu_array} ram: ${ram_array}`);
// if the cpu and ram arrays are null, create both arrays with 30 values of 0
if (cpu_array == null) { cpu_array = Array(30).fill(0); }
if (ram_array == null) { ram_array = Array(30).fill(0); }
console.log(`#2: ${name} cpu: ${cpu_array} ram: ${ram_array}`);
// add the new cpu and ram values to the arrays, but limit the array to 30 values
cpu_array.push(cpu);
ram_array.push(ram);
console.log(`#3: ${name} cpu: ${cpu_array} ram: ${ram_array}`);
cpu_array = cpu_array.slice(-30);
ram_array = ram_array.slice(-30);
console.log(`#4: ${name} cpu: ${cpu_array} ram: ${ram_array}`);
// save the arrays to local storage
localStorage.setItem(`${name}_cpu`, JSON.stringify(cpu_array));
localStorage.setItem(`${name}_ram`, JSON.stringify(ram_array));
// replace the old chart with the new one without breaking the surrounding elements
let chart = document.getElementById(`${name}_chart`);
let newChart = document.createElement('div');
newChart.id = `${name}_chart`;
chart.parentNode.replaceChild(newChart, chart);
drawCharts(`#${name}_chart`, cpu_array, ram_array);
});
socket.on('install', (data) => {
console.log('added install card');

View file

@ -4,15 +4,14 @@ const router = express.Router();
const { Dashboard } = require("../controllers/dashboard");
const { AddSite, RemoveSite, RefreshSites, DisableSite, EnableSite } = require("../controllers/site_actions");
const { Install, Uninstall } = require("../controllers/app_actions");
const {Apps, processApps} = require("../controllers/apps");
const { Apps, searchApps, Install, Uninstall } = require("../controllers/apps");
const { Users } = require("../controllers/users");
const {Account} = require("../controllers/account");
const {Settings} = require("../controllers/settings");
const {Logout} = require("../controllers/logout");
const {Login, processLogin} = require("../controllers/login");
const {Register, processRegister} = require("../controllers/register");
const { Account } = require("../controllers/account");
const { Settings } = require("../controllers/settings");
const { Logout } = require("../controllers/logout");
const { Login, processLogin } = require("../controllers/login");
const { Register, processRegister } = require("../controllers/register");
@ -31,7 +30,7 @@ router.post("/enablesite", EnableSite)
router.get("/users", Users);
router.get("/apps", Apps);
router.post("/apps", processApps);
router.post("/apps", searchApps);
router.get("/settings", Settings);
router.get("/account", Account);

View file

@ -337,7 +337,7 @@
"image": "ghcr.io/linuxserver/prowlarr:develop",
"categories": [
"Downloaders",
"Tools"
"Arr"
],
"ports": [
"9696/tcp"
@ -1144,7 +1144,7 @@
},
{
"categories": [
"Multimedia"
"Media"
],
"description": "Airsonic is a free, web-based media streamer, providing ubiqutious access to your music. Use it to share your music with friends, or to listen to your own music while at work. You can stream to multiple players simultaneously, for instance to one player in your kitchen and another in your living room.",
"env": [
@ -1371,7 +1371,7 @@
"description": "Audacity is an easy-to-use, multi-track audio editor and recorder. Developed by a group of volunteers as open source. (https://www.audacityteam.org/)",
"platform": "linux",
"categories": [
"Multimedia"
"Media"
],
"logo": "https://raw.githubusercontent.com/linuxserver/docker-templates/master/linuxserver.io/img/audacity-logo.png",
"image": "linuxserver/audacity:latest",
@ -2161,7 +2161,7 @@
"note": "",
"description": "Kasm workspaces is a docker container streaming platform for delivering browser-based access to desktops, applications, and web services. Kasm uses devops-enabled Containerized Desktop Infrastructure (CDI) to create on-demand, disposable, docker containers that are accessible via web browser. Example use-cases include Remote Browser Isolation (RBI), Data Loss Prevention (DLP), Desktop as a Service (DaaS), Secure Remote Access Services (RAS), and Open Source Intelligence (OSINT) collections. The rendering of the graphical-based containers is powered by the open-source project [KasmVNC](https://www.kasmweb.com/kasmvnc.html?utm_campaign=LinuxServer&utm_source=kasmvnc).",
"platform": "linux",
"logo": "https://kasm-ci.s3.amazonaws.com/kasm_wide.png",
"logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/kasm.png",
"image": "linuxserver/kasm:latest",
"privileged": true,
"env": [
@ -2196,20 +2196,20 @@
],
"volumes": [
{
"container": "/opt",
"bind": "/home/docker/kasm/opt"
"bind": "/home/docker/kasm/opt",
"container": "/opt"
},
{
"container": "/profiles",
"bind": "/home/docker/kasm/profiles"
"bind": "/home/docker/kasm/profiles",
"container": "/profiles"
},
{
"container": "/dev/input",
"bind": "/dev/input"
"bind": "/dev/input",
"container": "/dev/input"
},
{
"container": "/run/udev/data",
"bind": "/run/udev/data"
"bind": "/run/udev/data",
"container": "/run/udev/data"
}
],
"restart_policy": "unless-stopped"
@ -2225,7 +2225,7 @@
"Tools"
],
"platform": "linux",
"logo": "https://raw.githubusercontent.com/linuxserver/docker-templates/master/linuxserver.io/img/libreoffice-logo.png",
"logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/libreoffice.png",
"image": "linuxserver/libreoffice:latest",
"env": [
{
@ -2269,7 +2269,7 @@
"Communication"
],
"platform": "linux",
"logo": "https://raw.githubusercontent.com/linuxserver/docker-templates/master/linuxserver.io/img/mastodon-banner.png",
"logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/mastodon.png",
"image": "linuxserver/mastodon:latest",
"env": [
{
@ -2511,7 +2511,7 @@
"Tools"
],
"platform": "linux",
"logo": "https://raw.githubusercontent.com/linuxserver/docker-templates/master/linuxserver.io/img/phpmyadmin-logo.png",
"logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/phpmyadmin.png",
"image": "linuxserver/phpmyadmin:latest",
"env": [
{
@ -2561,9 +2561,9 @@
"title": "Pidgin",
"name": "pidgin",
"note": "",
"description": "Pidgin (https://pidgin.im/) is a chat program which lets you log into accounts on multiple chat networks simultaneously. This means that you can be chatting with friends on XMPP and sitting in an IRC channel at the same time.",
"description": "Pidgin is a chat program which lets you log into accounts on multiple chat networks simultaneously. This means that you can be chatting with friends on XMPP and sitting in an IRC channel at the same time.",
"platform": "linux",
"logo": "https://raw.githubusercontent.com/linuxserver/docker-templates/master/linuxserver.io/img/pidgin-logo.png",
"logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/pidgin.png",
"image": "linuxserver/pidgin:latest",
"env": [
{
@ -2591,8 +2591,8 @@
],
"volumes": [
{
"container": "/config",
"bind": "/home/docker/pidgin/config"
"bind": "/home/docker/pidgin",
"container": "/config"
}
],
"restart_policy": "unless-stopped"
@ -2602,9 +2602,9 @@
"title": "Remmina",
"name": "remmina",
"note": "",
"description": "[Remmina](https://remmina.org/) is a remote desktop client written in GTK, aiming to be useful for system administrators and travellers, who need to work with lots of remote computers in front of either large or tiny screens. Remmina supports multiple network protocols, in an integrated and consistent user interface. Currently RDP, VNC, SPICE, NX, XDMCP, SSH and EXEC are supported.",
"description": "Remmina is a remote desktop client written in GTK, aiming to be useful for system administrators and travellers, who need to work with lots of remote computers in front of either large or tiny screens. Remmina supports multiple network protocols, in an integrated and consistent user interface. Currently RDP, VNC, SPICE, NX, XDMCP, SSH and EXEC are supported.",
"platform": "linux",
"logo": "https://raw.githubusercontent.com/linuxserver/docker-templates/master/linuxserver.io/img/remmina-icon.png",
"logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/remmina.png",
"image": "linuxserver/remmina:latest",
"env": [
{
@ -3016,7 +3016,7 @@
"name": "PORT"
}
],
"logo": "https://raw.githubusercontent.com/walkxcode/dashboard-icons/main/png/authentik.png",
"logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/authentik.png",
"name": "authentik",
"platform": "linux",
"repository": {
@ -3063,8 +3063,8 @@
"categories": [
"Communication"
],
"description": "Rocket.Chat Server",
"logo": "https://raw.githubusercontent.com/portapps/rocketchat-portable/master/res/papp.png",
"description": "Rocket.Chat is an open-source fully customizable communications platform developed in JavaScript for organizations with high standards of data protection.",
"logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/rocketchat.png",
"note": "Rocket.Chat Server Container",
"platform": "linux",
"repository": {
@ -3720,8 +3720,8 @@
"Tools"
],
"description": "ClamAV is an open source antivirus engine for detecting trojans, viruses, malware & other malicious threats.",
"image": "mkodockx/docker-clamav:alpine",
"logo": "http://www.clamav.net/assets/clamav-trademark.png",
"image": "clamav/clamav",
"logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/clamav.png",
"name": "clamav",
"platform": "linux",
"ports": [
@ -3733,19 +3733,15 @@
"volumes": [
{
"bind": "/etc/timezone",
"container": "/etc/timezone"
},
{
"bind": "/etc/localtime",
"container": "/etc/localtime"
},
{
"bind": "/home/docker/clamav/config",
"container": "/etc/clamav"
"container": "/etc/timezone:ro"
},
{
"bind": "/home/docker/clamav/virus_definitions",
"container": "/var/lib/clamav"
},
{
"bind": "/home/docker",
"container": "/scandir"
}
],
"note": ""
@ -4935,7 +4931,7 @@
{
"categories": [
"Multimedia",
"AI"
"Productivity"
],
"description": "PhotoPrism is an AI-powered app for browsing, organizing & sharing your photo collection. It makes use of the latest technologies to tag and find pictures automatically without getting in your way.| Copy as Custom stack and EDIT environment variables.",
"logo": "https://photoprism.app/static/img/logo.svg",
@ -5721,7 +5717,7 @@
"CMS"
],
"platform": "linux",
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/joomla.png",
"logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/joomla.png",
"image": "joomla:latest",
"env": [
{

View file

@ -59,7 +59,7 @@
<div class="navbar-nav flex-row order-md-last">
<div class="nav-item d-none d-md-flex me-3">
<div class="btn-list">
<a href="#" class="btn text-white">
<a href="#" class="btn text-black">
<!-- Download SVG icon from http://tabler-icons.io/i/lock -->
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-lock" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M5 13a2 2 0 0 1 2 -2h10a2 2 0 0 1 2 2v6a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2 -2v-6z"></path> <path d="M11 16a1 1 0 1 0 2 0a1 1 0 0 0 -2 0"></path> <path d="M8 11v-4a4 4 0 1 1 8 0v4"></path> </svg>
VPN
@ -69,7 +69,7 @@
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-shield" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M12 3a12 12 0 0 0 8.5 3a12 12 0 0 1 -8.5 15a12 12 0 0 1 -8.5 -15a12 12 0 0 0 8.5 -3"></path> </svg>
Firewall
</a>
<a href="#" class="btn text-white">
<a href="#" class="btn text-black">
<!-- Download SVG icon from http://tabler-icons.io/i/shield -->
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-screen-share" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M21 12v3a1 1 0 0 1 -1 1h-16a1 1 0 0 1 -1 -1v-10a1 1 0 0 1 1 -1h9"></path> <path d="M7 20l10 0"></path> <path d="M9 16l0 4"></path> <path d="M15 16l0 4"></path> <path d="M17 4h4v4"></path> <path d="M16 9l5 -5"></path> </svg>
VNC