Merge pull request #9 from lllllllillllllillll/dev

Merge dev into main for v0.04
This commit is contained in:
lllllllillllllillll 2023-11-11 00:06:33 -08:00 committed by GitHub
commit 39bb0485b9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 3038 additions and 138 deletions

View file

@ -1,3 +1,9 @@
## v0.04 (Nov 11th 2023)
* Docker Image and Compose file available.
* The containers DweebUI and DweebCache are hidden from the dashboard.
* Default icon for containers.
* Fixed missing information in container details/edit modals (Ports, Env, Volumes, Labels).
## v0.03 (Nov 5th 2023)
* Container graphs now load instantly on refresh
* Working net data for server dashboard

36
Dockerfile Normal file
View file

@ -0,0 +1,36 @@
# syntax=docker/dockerfile:1
# Comments are provided throughout this file to help you get started.
# If you need more help, visit the Dockerfile reference guide at
# https://docs.docker.com/engine/reference/builder/
ARG NODE_VERSION=20.0.0
FROM node:${NODE_VERSION}-alpine
# Use production node environment by default.
ENV NODE_ENV production
WORKDIR /app
# Download dependencies as a separate step to take advantage of Docker's caching.
# Leverage a cache mount to /root/.npm to speed up subsequent builds.
# Leverage a bind mounts to package.json and package-lock.json to avoid having to copy them into
# into this layer.
RUN --mount=type=bind,source=package.json,target=package.json \
--mount=type=bind,source=package-lock.json,target=package-lock.json \
--mount=type=cache,target=/root/.npm \
npm ci --omit=dev
# Run the application as a non-root user.
USER root
# Copy the rest of the source files into the image.
COPY . .
# Expose the port that the application listens on.
EXPOSE 8000
# Run the application.
CMD node app.js

View file

@ -3,13 +3,13 @@
DweebUI is a simple Docker web interface created with javascript and node.js
Pre-Pre-Pre-Pre-Pre Alpha v 0.03 ( :fire: Experimental. Don't install on any servers you care about :fire: )
Pre-Pre-Pre-Pre-Pre Alpha v 0.04 ( :fire: Experimental. Don't install on any servers you care about :fire: )
* I haven't used Github very much and I'm still new to javascript
* I haven't used Github very much and I'm still new to javascript.
* This is the first project I've ever released and I'm sure it's full of plenty of bugs and mistakes.
* I probably should have waited a lot longer to share this :|
Requirements: Fresh Install of Debian 12.2
Requirements: Docker
![DweebUI](https://raw.githubusercontent.com/lllllllillllllillll/DweebUI/main/DweebUI.png)
@ -26,8 +26,37 @@ Requirements: Fresh Install of Debian 12.2
## Setup
* Download and extract DweebUI.zip to a fresh Debian 12.2 Install
* Docker compose.yaml:
```
services:
  dweebui:
    container_name: DweebUI
    image: lllllllillllllillll/dweebui:v0.04
    ports:
      - 8000:8000
    depends_on:
      - cache
    links:
      - cache
    volumes:
      - dweebui:/app
      - /var/run/docker.sock:/var/run/docker.sock
  cache:
    container_name: DweebCache
    image: redis:6.2-alpine
    restart: always
    command: redis-server --save 20 1 --loglevel warning --requirepass eYVX7EwVmmxKPCDmwMtyKVge8oLd2t81
    volumes:
      - cache:/data
 
volumes:
  dweebui:
  cache:
```
* Using setup.sh:
```
Extract DweebUI.zip and navigate to /DweebUI
cd DweebUI
chmod +x setup.sh
sudo ./setup.sh

5
app.js
View file

@ -9,9 +9,8 @@ const { serverStats, containerList, containerStats, containerAction } = require(
let sent_list, clicked;
const redisClient = require('redis').createClient({
host:'localhost',
port:6379,
password:'somesupersecretpassword',
url: 'redis://DweebCache:6379',
password:'eYVX7EwVmmxKPCDmwMtyKVge8oLd2t81',
legacyMode:true
});
redisClient.connect().catch(console.log);

View file

@ -1,6 +1,6 @@
module.exports.dashCard = function dashCard(data) {
let { name, service, id, state, image, external_port, internal_port } = data;
let { name, service, id, state, image, external_port, internal_port, ports, volumes, environment_variables, labels } = data;
//disable controls for a docker container depending on its name
let enabled = "";
@ -32,77 +32,90 @@ module.exports.dashCard = function dashCard(data) {
let restart_policy = 'unless-stopped';
let ports_data = [];
for (let i = 0; i < 12; i++) {
if (ports) {
ports_data = ports;
} else {
for (let i = 0; i < 12; i++) {
let port_check = "checked";
let external = i;
let internal = i;
let protocol = "tcp";
let port_check = "checked";
let external = i;
let internal = i;
let protocol = "tcp";
ports_data.push({
check: port_check,
external: external,
internal: internal,
protocol: protocol
});
ports_data.push({
check: port_check,
external: external,
internal: internal,
protocol: protocol
});
}
}
let volumes_data = [];
for (let i = 0; i < 12; i++) {
if (volumes) {
volumes_data = volumes;
} else {
for (let i = 0; i < 12; i++) {
let vol_check = "checked";
let bind = i;
let container = i;
let readwrite = "rw";
let vol_check = "checked";
let bind = i;
let container = i;
let readwrite = "rw";
volumes_data.push({
check: vol_check,
bind: bind,
container: container,
readwrite: readwrite
});
volumes_data.push({
check: vol_check,
bind: bind,
container: container,
readwrite: readwrite
});
}
}
let env_data = [];
for (let i = 0; i < 12; i++) {
if (environment_variables) {
env_data = environment_variables;
} else {
for (let i = 0; i < 12; i++) {
let env_check = "checked";
let env_name = i;
let env_default = i;
let env_check = "checked";
let env_name = i;
let env_default = i;
env_data.push({
env_check: env_check,
env_name: env_name,
env_default: env_default
});
env_data.push({
check: env_check,
name: env_name,
default: env_default
});
}
}
let label_data = [];
for (let i = 0; i < 12; i++) {
if (labels) {
label_data = labels;
} else {
for (let i = 0; i < 12; i++) {
let label_check = "checked";
let label_name = i;
let label_default = i;
label_data.push({
label_check: label_check,
label_name: label_name,
label_default: label_default
});
let label_check = "checked";
let label_name = i;
let label_default = i;
label_data.push({
check: label_check,
name: label_name,
value: label_default
});
}
}
return `
<div class="col-sm-6 col-lg-3 deleteme">
<div class="card">
<div class="card-body">
<div class="card-stamp card-stamp-sm">
<img heigh="150px" width="150px" src="https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/${service}.png"></img>
<img heigh="150px" width="150px" src="https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/${service}.png" onerror="this.onerror=null;this.src='https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/dweebui.png';"></img>
</div>
<div class="d-flex align-items-center">

27
compose.yaml Normal file
View file

@ -0,0 +1,27 @@
services:
dweebui:
container_name: DweebUI
build:
context: .
environment:
NODE_ENV: production
ports:
- 8000:8000
depends_on:
- cache
links:
- cache
volumes:
- dweebui:/app
- /var/run/docker.sock:/var/run/docker.sock
cache:
container_name: DweebCache
image: redis:6.2-alpine
restart: always
command: redis-server --save 20 1 --loglevel warning --requirepass eYVX7EwVmmxKPCDmwMtyKVge8oLd2t81
volumes:
- cache:/data
volumes:
dweebui:
cache:

View file

@ -1,11 +1,10 @@
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 { install, uninstall } = require('../functions/package_manager');
// import { install, uninstall } from '../functions/package_manager';
const templates_json = require('../templates.json');
let templates = templates_json.templates;
@ -29,7 +28,6 @@ exports.Apps = async function(req, res) {
let list_end = (page * 28);
let last_page = Math.ceil(templates.length / 28);
// generate values for prev and next buttons so that i can go back and forth between pages
let prev = '/apps?page=' + (page - 1);
let next = '/apps?page=' + (page + 1);
if (page == 1) {
@ -78,7 +76,6 @@ exports.searchApps = async function(req, res) {
let list_end = (page * 28);
let last_page = Math.ceil(templates.length / 28);
// generate values for prev and next buttons so that i can go back and forth between pages
let prev = '/apps?page=' + (page - 1);
let next = '/apps?page=' + (page + 1);
if (page == 1) {
@ -95,7 +92,6 @@ exports.searchApps = async function(req, res) {
// split value of search into an array of words
search = search.split(' ');
try {console.log(search[0]);} catch (error) {}
try {console.log(search[1]);} catch (error) {}
try {console.log(search[2]);} catch (error) {}
@ -179,11 +175,9 @@ exports.Uninstall = async function (req, res) {
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}`);
});
uninstall(req.body);
}

View file

@ -51,8 +51,6 @@ exports.processRegister = async function(req,res){
avatar: `<img src="./static/avatars/${avatar}">`
});
console.log(`Created: ${user.first_name}`);
// set the session.
req.session.user = user.username;
req.session.UUID = user.UUID;

View file

@ -2,7 +2,7 @@ const { Sequelize, DataTypes } = require('sequelize');
const sequelize = new Sequelize({
dialect: 'sqlite',
storage: 'database/db.sqlite',
storage: './database/db.sqlite',
logging: false
});

View file

@ -2,7 +2,7 @@ const { Sequelize, DataTypes } = require('sequelize');
const sequelize = new Sequelize({
dialect: 'sqlite',
storage: 'database/db.sqlite',
storage: './database/db.sqlite',
logging: false
});

View file

@ -1,8 +1,10 @@
const { writeFileSync, mkdirSync, readFileSync } = require("fs");
const { exec, execSync } = require("child_process");
const { dashCard } = require('../components/dashCard');
const yaml = require('js-yaml');
const { exec, execSync } = require("child_process");
const { docker } = require('./system_information');
var DockerodeCompose = require('dockerode-compose');
module.exports.install = async function (data) {
@ -124,13 +126,18 @@ module.exports.install = async function (data) {
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 {
var compose = new DockerodeCompose(docker, `./appdata/${name}/docker-compose.yml`, `${name}`);
(async () => {
await compose.pull();
await compose.up();
})();
} catch { console.log('error running compose file')}
}
@ -140,22 +147,25 @@ module.exports.install = async function (data) {
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}`);
});
var containerName = docker.getContainer(`${data.service_name}`);
try {
containerName.stop(function (err, data) {
});
} catch { console.log('unable to stop container') }
try {
containerName.remove(function (err, data) {
});
} catch { console.log('unable to remove container') }
}
// Redirect to the home page
res.redirect("/");
} else {
// Redirect to the login page
res.redirect("/login");
}
}

View file

@ -3,7 +3,8 @@ var Docker = require('dockerode');
var docker = new Docker({ socketPath: '/var/run/docker.sock' });
const { dashCard } = require('../components/dashCard');
// export docker
module.exports.docker = docker;
module.exports.serverStats = async function () {
const cpuUsage = await currentLoad();
@ -29,55 +30,128 @@ module.exports.containerList = async function () {
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;
if ((container.Names[0].slice(1) != 'DweebUI') && (container.Names[0].slice(1) != 'DweebCache')) {
for (const [key, value] of Object.entries(containerInfo.HostConfig.PortBindings)) {
open_ports.push(`${value[0].HostPort}`);
external_port = value[0].HostPort;
internal_port = key;
let imageVersion = container.Image.split('/');
let service = imageVersion[imageVersion.length - 1].split(':')[0];
if ((external_port == undefined) || (internal_port == undefined)) {
external_port = 0;
internal_port = 0;
let containerId = docker.getContainer(container.Id);
let containerInfo = await containerId.inspect();
let external_port = 0;
let internal_port = 0;
// Get ports
let ports_list = [];
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);
}
for (let i = 0; i < 12; i++) {
if (ports_list[i] == undefined) {
let ports = {
check : '',
external: '',
internal: '',
protocol: ''
}
ports_list[i] = ports;
}
}
// Get volumes.
let volumes_list = [];
for (const [key, value] of Object.entries(containerInfo.HostConfig.Binds)) {
let volumes = {
check : 'checked',
bind: value.split(':')[0],
container: value.split(':')[1],
readwrite: value.split(':')[2]
}
volumes_list.push(volumes);
}
for (let i = 0; i < 12; i++) {
if (volumes_list[i] == undefined) {
let volumes = {
check : '',
bind: '',
container: '',
readwrite: ''
}
volumes_list[i] = volumes;
}
}
// Get environment variables.
let environment_variables = [];
for (const [key, value] of Object.entries(containerInfo.Config.Env)) {
let env = {
check : 'checked',
name: value.split('=')[0],
default: value.split('=')[1]
}
environment_variables.push(env);
}
for (let i = 0; i < 12; i++) {
if (environment_variables[i] == undefined) {
let env = {
check : '',
name: '',
default: ''
}
environment_variables[i] = env;
}
}
// Get labels.
let labels = [];
for (const [key, value] of Object.entries(containerInfo.Config.Labels)) {
let label = {
check : 'checked',
name: key,
value: value
}
labels.push(label);
}
for (let i = 0; i < 12; i++) {
if (labels[i] == undefined) {
let label = {
check : '',
name: '',
value: ''
}
labels[i] = label;
}
}
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,
ports: ports_list,
volumes: volumes_list,
environment_variables: environment_variables,
labels: labels,
}
let dockerCard = dashCard(container_info);
card_list += dockerCard;
}
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;
}

2713
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -10,15 +10,16 @@
"child_process": "^1.0.2",
"connect-redis": "^6.1.3",
"dockerode": "^3.3.5",
"dockerode-compose": "^1.4.0",
"ejs": "^3.1.9",
"express": "^4.18.2",
"express-session": "^1.17.3",
"js-yaml": "^4.1.0",
"redis": "^4.6.5",
"sequelize": "^6.32.1",
"socket.io": "^4.6.1",
"sqlite3": "^5.1.6",
"systeminformation": "^5.17.12",
"js-yaml": "^4.1.0"
"systeminformation": "^5.17.12"
},
"description": ""
}

View file

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

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-black">
<a href="#" class="btn text-green">
<!-- Download SVG icon from http://tabler-icons.io/i/lock -->
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-lock" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M5 13a2 2 0 0 1 2 -2h10a2 2 0 0 1 2 2v6a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2 -2v-6z"></path> <path d="M11 16a1 1 0 1 0 2 0a1 1 0 0 0 -2 0"></path> <path d="M8 11v-4a4 4 0 1 1 8 0v4"></path> </svg>
VPN
@ -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-black">
<a href="#" class="btn text-green">
<!-- Download SVG icon from http://tabler-icons.io/i/shield -->
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-screen-share" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M21 12v3a1 1 0 0 1 -1 1h-16a1 1 0 0 1 -1 -1v-10a1 1 0 0 1 1 -1h9"></path> <path d="M7 20l10 0"></path> <path d="M9 16l0 4"></path> <path d="M15 16l0 4"></path> <path d="M17 4h4v4"></path> <path d="M16 9l5 -5"></path> </svg>
VNC