+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/compose.yaml b/compose.yaml
index 20ab0e3..b982d32 100644
--- a/compose.yaml
+++ b/compose.yaml
@@ -1,10 +1,8 @@
services:
dweebui:
container_name: DweebUI
- build:
- context: .
- environment:
- NODE_ENV: production
+ image: lllllllillllllillll/dweebui:v0.05
+ restart: unless-stopped
ports:
- 8000:8000
depends_on:
@@ -13,6 +11,8 @@ services:
- cache
volumes:
- dweebui:/app
+ - ./caddyfiles/Caddyfile:/app/caddyfiles/Caddyfile
+ - ./caddyfiles/sites:/app/caddyfiles/sites
- /var/run/docker.sock:/var/run/docker.sock
cache:
container_name: DweebCache
@@ -21,7 +21,20 @@ services:
command: redis-server --save 20 1 --loglevel warning --requirepass eYVX7EwVmmxKPCDmwMtyKVge8oLd2t81
volumes:
- cache:/data
-
+ proxy:
+ container_name: DweebProxy
+ image: caddy:2.4.5-alpine
+ depends_on:
+ - dweebui
+ restart: unless-stopped
+ network_mode: host
+ volumes:
+ - caddy:/data
+ - caddy:/config
+ - ./caddyfiles/Caddyfile:/etc/caddy/Caddyfile
+ - ./caddyfiles/sites:/etc/caddy/sites
+
volumes:
dweebui:
- cache:
\ No newline at end of file
+ cache:
+ caddy:
\ No newline at end of file
diff --git a/controllers/apps.js b/controllers/apps.js
index 548d820..a83b216 100644
--- a/controllers/apps.js
+++ b/controllers/apps.js
@@ -144,6 +144,8 @@ exports.Install = async function (req, res) {
if (req.session.role == "admin") {
+ console.log(`Starting install for: ${req.body.name}`)
+
install(req.body);
let container_info = {
diff --git a/controllers/dashboard.js b/controllers/dashboard.js
index 3e9e6c9..b77a0ee 100644
--- a/controllers/dashboard.js
+++ b/controllers/dashboard.js
@@ -13,7 +13,8 @@ exports.Dashboard = async function (req, res) {
name: user.first_name + ' ' + user.last_name,
role: user.role,
avatar: user.avatar,
- isLoggedIn: true
+ isLoggedIn: true,
+ site_list: req.app.locals.site_list,
});
} else {
// Redirect to the login page
diff --git a/controllers/site_actions.js b/controllers/site_actions.js
index f038898..2d569a3 100644
--- a/controllers/site_actions.js
+++ b/controllers/site_actions.js
@@ -1,7 +1,7 @@
const { readFileSync, writeFileSync, appendFileSync, readdirSync } = require('fs');
const { execSync } = require("child_process");
const { siteCard } = require('../components/siteCard');
-
+const { containerExec } = require('../functions/system_information')
exports.AddSite = async function (req, res) {
@@ -20,31 +20,54 @@ exports.AddSite = async function (req, res) {
caddyfile += `\n\t}`
caddyfile += `\n}`
- // save caddyfile
- writeFileSync(`/home/docker/caddy/sites/${domain}.Caddyfile`, caddyfile, function (err) { console.log(err) });
+ // save caddyfile
+ writeFileSync(`./caddyfiles/sites/${domain}.Caddyfile`, caddyfile, function (err) { console.log(err) });
+
// format caddyfile
- execSync(`docker exec caddy caddy fmt --overwrite /etc/caddy/sites/${domain}.Caddyfile`, (err, stdout, stderr) => {
- if (err) { console.error(`error: ${err.message}`); return; }
- if (stderr) { console.error(`stderr: ${stderr}`); return; }
- if (stdout) { console.log(`stdout:\n${stdout}`); return; }
- console.log(`Formatted ${domain}.Caddyfile`)
+ let format = {
+ container: 'DweebProxy',
+ command: `caddy fmt --overwrite /etc/caddy/sites/${domain}.Caddyfile`
+ }
+ await containerExec(format, function(err, data) {
+ if (err) {
+ console.error(err);
+ return;
+ }
+ console.log(`Formatted ${domain}.Caddyfile`);
});
+ ///////////////// convert caddyfile to json
+ let convert = {
+ container: 'DweebProxy',
+ command: `caddy adapt --config /etc/caddy/sites/${domain}.Caddyfile --pretty >> /etc/caddy/sites/${domain}.json`
+ }
+ await containerExec(convert, function(err, data) {
+ if (err) {
+ console.error(err);
+ return;
+ }
+ console.log(`Converted ${domain}.Caddyfile to JSON`);
+ });
+
+ ////////////// reload caddy
+ let reload = {
+ container: 'DweebProxy',
+ command: `caddy reload --config /etc/caddy/Caddyfile`
+ }
+ await containerExec(reload, function(err, data) {
+ if (err) {
+ console.error(err);
+ return;
+ }
+ console.log(`Reloaded Caddy Config`);
+ });
let site = siteCard(type, domain, host, port, 0);
- // reload caddy config to enable new site
- execSync(`docker exec caddy caddy reload --config /etc/caddy/Caddyfile`, (err, stdout, stderr) => {
- if (err) { console.error(`error: ${err.message}`); return; }
- if (stderr) { console.error(`stderr: ${stderr}`); return; }
- if (stdout) { console.log(`stdout:\n${stdout}`); return; }
- console.log(`reloaded caddy config`)
- });
+ req.app.locals.site_list += site;
- // append the site to site_list.ejs
- appendFileSync('./views/partials/site_list.ejs', site, function (err) { console.log(err) });
res.redirect("/");
} else {
@@ -61,22 +84,20 @@ exports.RemoveSite = async function (req, res) {
for (const [key, value] of Object.entries(req.body)) {
- console.log(`${key}: ${value}`);
- execSync(`rm /home/docker/caddy/sites/${value}.Caddyfile`, (err, stdout, stderr) => {
+
+ execSync(`rm ./caddyfiles/sites/${value}.Caddyfile`, (err, stdout, stderr) => {
if (err) { console.error(`error: ${err.message}`); return; }
if (stderr) { console.error(`stderr: ${stderr}`); return; }
console.log(`removed ${value}.Caddyfile`);
});
+
}
-
- // reload caddy config to disable sites
- try {
- execSync(`docker exec caddy caddy reload --config /etc/caddy/Caddyfile`, (err, stdout, stderr) => {
- if (err) { console.error(`error: ${err.message}`); return; }
- if (stderr) { console.error(`stderr: ${stderr}`); return; }
- console.log(`reloaded caddy config`)
- }); } catch (error) { console.log("No sites to reload") }
+ let reload = {
+ container: 'DweebProxy',
+ command: `caddy reload --config /etc/caddy/Caddyfile`
+ }
+ await containerExec(reload);
console.log('Removed Site(s)')
@@ -98,21 +119,15 @@ exports.RefreshSites = async function (req, res) {
// Clear site_list.ejs
- writeFileSync('./views/partials/site_list.ejs', '', function (err) {
- if (err) {
- console.log(err);
- } else {
- console.log('site_list.ejs has been cleared');
- }
- });
+ req.app.locals.site_list = "";
- // check if /home/docker/caddy/sites/ contains any .json files, then delete them
+ // check if ./caddyfiles/sites contains any .json files, then delete them
try {
- let files = readdirSync('/home/docker/caddy/sites/');
+ let files = readdirSync('./caddyfiles/sites/');
files.forEach(file => {
if (file.includes(".json")) {
- execSync(`rm /home/docker/caddy/sites/${file}`, (err, stdout, stderr) => {
+ execSync(`rm ./caddyfiles/sites/${file}`, (err, stdout, stderr) => {
if (err) { console.error(`error: ${err.message}`); return; }
if (stderr) { console.error(`stderr: ${stderr}`); return; }
console.log(`removed ${file}`);
@@ -122,23 +137,25 @@ exports.RefreshSites = async function (req, res) {
} catch (error) { console.log("No .json files to delete") }
// get list of Caddyfiles
- let sites = readdirSync('/home/docker/caddy/sites/');
+ let sites = readdirSync('./caddyfiles/sites/');
- sites.forEach(site_name => {
+ sites.forEach(site_name => {
// convert the caddyfile of each site to json
- execSync(`docker exec caddy caddy adapt --config /etc/caddy/sites/${site_name} --pretty >> /home/docker/caddy/sites/${site_name}.json`, (err, stdout, stderr) => {
- if (err) { console.error(`error: ${err.message}`); return; }
- if (stderr) { console.error(`stderr: ${stderr}`); return; }
- console.log(`stdout:\n${stdout}`);
- });
-
- // read the json file
- let site_file = readFileSync(`/home/docker/caddy/sites/${site_name}.json`, 'utf8');
+ let convert = {
+ container: 'DweebProxy',
+ command: `caddy adapt --config ./caddyfiles/sites/${site_name} --pretty >> ./caddyfiles/sites/${site_name}.json`
+ }
+ containerExec(convert);
+ try {
+ // read the json file
+ let site_file = readFileSync(`./caddyfiles/sites/${site_name}.json`, 'utf8');
// fix whitespace and parse the json file
site_file = site_file.replace(/ /g, " ");
site_file = JSON.parse(site_file);
+ } catch (error) { console.log("No .json file to read") }
+
// get the domain, type, host, and port from the json file
try { domain = site_file.apps.http.servers.srv0.routes[0].match[0].host[0] } catch (error) { console.log("No Domain") }
@@ -149,13 +166,13 @@ exports.RefreshSites = async function (req, res) {
// build the site card
let site = siteCard(type, domain, host, port, id);
- // append the site card to site_list.ejs
- appendFileSync('./views/partials/site_list.ejs', site, function (err) { console.log(err) });
+ // append the site card to site_list
+ req.app.locals.site_list += site;
id++;
-
});
+
res.redirect("/");
} else {
// Redirect to the login page
diff --git a/functions/package_manager.js b/functions/package_manager.js
index 29f7581..31fe1a0 100644
--- a/functions/package_manager.js
+++ b/functions/package_manager.js
@@ -9,6 +9,7 @@ var DockerodeCompose = require('dockerode-compose');
module.exports.install = async function (data) {
+ console.log(`[Start of install function]`);
let { service_name, name, image, command_check, command, net_mode, restart_policy } = data;
let { port0, port1, port2, port3, port4, port5 } = data;
@@ -16,6 +17,8 @@ module.exports.install = async function (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;
+ let docker_volumes = [];
+
if (image.startsWith('https://')){
mkdirSync(`./appdata/${name}`, { recursive: true });
execSync(`curl -o ./appdata/${name}/${name}_stack.yml -L ${image}`);
@@ -71,9 +74,18 @@ module.exports.install = async function (data) {
compose_file += `\n volumes:`
for (let i = 0; i < 6; i++) {
- if (data[`volume${i}`] == 'on') {
+
+ // if volume is on and neither bind or container is empty, it's a bind mount (ex /mnt/user/appdata/config:/config )
+ if ((data[`volume${i}`] == 'on') && (data[`volume_${i}_bind`] != '') && (data[`volume_${i}_container`] != '')) {
compose_file += `\n - ${data[`volume_${i}_bind`]}:${data[`volume_${i}_container`]}:${data[`volume_${i}_readwrite`]}`
}
+
+ // if bind is empty create a docker volume (ex container_name_config:/config) convert any '/' in container name to '_'
+ else if ((data[`volume${i}`] == 'on') && (data[`volume_${i}_bind`] == '') && (data[`volume_${i}_container`] != '')) {
+ let volume_name = data[`volume_${i}_container`].replace(/\//g, '_');
+ compose_file += `\n - ${name}_${volume_name}:${data[`volume_${i}_container`]}:${data[`volume_${i}_readwrite`]}`
+ docker_volumes.push(`${name}_${volume_name}`);
+ }
}
}
@@ -122,6 +134,22 @@ module.exports.install = async function (data) {
}
}
+
+ // add any docker volumes to the docker-compose file
+ if ( docker_volumes.length > 0 ) {
+ compose_file += `\n`
+ compose_file += `\nvolumes:`
+
+ // check docker_volumes for duplicates and remove them completely
+ docker_volumes = docker_volumes.filter((item, index) => docker_volumes.indexOf(item) === index)
+
+ for (let i = 0; i < docker_volumes.length; i++) {
+ if ( docker_volumes[i] != '') {
+ compose_file += `\n ${docker_volumes[i]}:`
+ }
+ }
+ }
+
try {
mkdirSync(`./appdata/${name}`, { recursive: true });
writeFileSync(`./appdata/${name}/docker-compose.yml`, compose_file, function (err) { console.log(err) });
@@ -154,16 +182,16 @@ module.exports.uninstall = async function (data) {
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') }
-
+ containerName.stop(function (err, data) {
+ if (data) {
+ containerName.remove(function (err, data) {
+ });
+ }
+ });
+ } catch {
+ containerName.remove(function (err, data) {
+ });
+ }
}
diff --git a/functions/system_information.js b/functions/system_information.js
index a7a0734..c34498a 100644
--- a/functions/system_information.js
+++ b/functions/system_information.js
@@ -32,7 +32,7 @@ module.exports.containerList = async function () {
for (const container of data) {
- if ((container.Names[0].slice(1) != 'DweebUI') && (container.Names[0].slice(1) != 'DweebCache')) {
+ if ((container.Names[0].slice(1) != 'DweebUI') && (container.Names[0].slice(1) != 'DweebCache') && (container.Names[0].slice(1) != 'DweebProxy')) {
let imageVersion = container.Image.split('/');
let service = imageVersion[imageVersion.length - 1].split(':')[0];
@@ -40,20 +40,20 @@ module.exports.containerList = async function () {
let containerId = docker.getContainer(container.Id);
let containerInfo = await containerId.inspect();
- let external_port = 0;
- let internal_port = 0;
-
- // Get ports
+ // 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]
+ 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);
}
- ports_list.push(ports);
- }
+ } catch { console.log('no ports') }
+
for (let i = 0; i < 12; i++) {
if (ports_list[i] == undefined) {
let ports = {
@@ -64,20 +64,20 @@ module.exports.containerList = async function () {
}
ports_list[i] = ports;
}
- }
+ } /////////////////////////////////////
- // Get volumes.
+ // 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);
- }
+ try { 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);
+ }} catch { console.log('no volumes') }
for (let i = 0; i < 12; i++) {
if (volumes_list[i] == undefined) {
let volumes = {
@@ -88,19 +88,19 @@ module.exports.containerList = async function () {
}
volumes_list[i] = volumes;
}
- }
+ } /////////////////////////////////////
// Get environment variables.
let environment_variables = [];
- for (const [key, value] of Object.entries(containerInfo.Config.Env)) {
+ try { 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);
- }
+ }} catch { console.log('no env') }
for (let i = 0; i < 12; i++) {
if (environment_variables[i] == undefined) {
let env = {
@@ -140,8 +140,8 @@ module.exports.containerList = async function () {
id: container.Id,
state: container.State,
image: container.Image,
- external_port: external_port,
- internal_port: internal_port,
+ external_port: ports_list[0].external || 0,
+ internal_port: ports_list[0].internal || 0,
ports: ports_list,
volumes: volumes_list,
environment_variables: environment_variables,
@@ -172,15 +172,17 @@ module.exports.containerStats = async function () {
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)
- }
+ if ((container.Names[0].slice(1) != 'DweebUI') && (container.Names[0].slice(1) != 'DweebCache') && (container.Names[0].slice(1) != 'DweebProxy')) {
+ 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);
+ //push stats to an array
+ container_stats.push(container_stat);
+ }
}
return container_stats;
}
@@ -219,5 +221,36 @@ module.exports.containerAction = async function (data) {
+module.exports.containerExec = async function (data) {
+
+ let { container, command } = data;
+
+ var containerName = docker.getContainer(container);
+
+ var options = {
+ Cmd: ['/bin/sh', '-c', command],
+ AttachStdout: true,
+ AttachStderr: true,
+ Tty: true
+ };
+
+ containerName.exec(options, function (err, exec) {
+ if (err) return;
+
+ exec.start(function (err, stream) {
+ if (err) return;
+
+ containerName.modem.demuxStream(stream, process.stdout, process.stderr);
+
+ exec.inspect(function (err, data) {
+ if (err) return;
+
+
+ });
+ });
+ });
+
+}
+
diff --git a/public/js/main.js b/public/js/main.js
index 6198de3..2b8797a 100644
--- a/public/js/main.js
+++ b/public/js/main.js
@@ -115,6 +115,10 @@ function buttonAction(button) {
socket.emit('clicked', {container: button.name, state: button.id, action: button.value});
}
+function viewLogs(button) {
+ console.log(`trying to view logs for ${button.name}`);
+}
+
socket.on('cards', (data) => {
console.log('cards deleted');
@@ -143,31 +147,31 @@ 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`));
- // 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); }
- // 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);
-
+
cpu_array = cpu_array.slice(-30);
ram_array = ram_array.slice(-30);
- // 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
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);
+ if (chart) {
+ let newChart = document.createElement('div');
+ newChart.id = `${name}_chart`;
+ chart.parentNode.replaceChild(newChart, chart);
+ drawCharts(`#${name}_chart`, cpu_array, ram_array);
+ } else {
+ console.log(`Chart element with id ${name}_chart not found in the DOM`);
+ }
});
socket.on('install', (data) => {
diff --git a/templates.json b/templates.json
index 98d2148..95cc4ad 100644
--- a/templates.json
+++ b/templates.json
@@ -910,18 +910,13 @@
"name": "PGID",
"set": "1000"
},
- {
- "label": "UMASK_SET",
- "name": "UMASK_SET",
- "set": "000"
- },
{
"label": "TZ",
"name": "TZ",
"set": "America/Chicago"
}
],
- "image": "linuxserver/deluge:latest",
+ "image": "lscr.io/linuxserver/deluge:latest",
"logo": "https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/deluge.png",
"platform": "linux",
"title": "Deluge",
@@ -934,6 +929,11 @@
{
"container": "/downloads"
}
+ ],
+ "ports": [
+ "8112/tcp",
+ "6881/tcp",
+ "6881/udp"
]
},
{
@@ -3705,11 +3705,11 @@
"container": "/data"
},
{
- "bind": "/home/docker/caddy/Caddyfile",
+ "bind": "caddy/Caddyfile",
"container": "/etc/caddy/Caddyfile"
},
{
- "bind": "/home/docker/caddy/sites",
+ "bind": "caddy/sites",
"container": "/etc/caddy/sites"
}
]
diff --git a/views/pages/dashboard.ejs b/views/pages/dashboard.ejs
index 5927d94..6805ffb 100644
--- a/views/pages/dashboard.ejs
+++ b/views/pages/dashboard.ejs
@@ -224,7 +224,7 @@
- <%- include('../partials/site_list.ejs') %>
+ <%- site_list %>
@@ -248,7 +248,7 @@
-
Imported: /home/docker/caddy/Caddyfile
+
./caddyfiles/Caddyfile