diff --git a/compose.yaml b/compose.yaml
index 122a373..11cf584 100644
--- a/compose.yaml
+++ b/compose.yaml
@@ -1,31 +1,19 @@
services:
dweebui:
container_name: DweebUI
- image: lllllllillllllillll/dweebui:v0.06
+ image: lllllllillllllillll/dweebui:v0.07
environment:
NODE_ENV: production
- REDIS_PASS: replace_with_password_for_redis
+ PORT: 8000
# Proxy_Manager: enabled
restart: unless-stopped
ports:
- 8000:8000
- depends_on:
- - cache
- links:
- - cache
volumes:
- dweebui:/app
- caddyfiles:/app/caddyfiles
- /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 replace_with_password_for_redis
- volumes:
- - cache:/data
volumes:
dweebui:
- cache:
caddyfiles:
diff --git a/controllers/register.js b/controllers/auth.js
similarity index 57%
rename from controllers/register.js
rename to controllers/auth.js
index a2b3b5e..b4c176a 100644
--- a/controllers/register.js
+++ b/controllers/auth.js
@@ -2,6 +2,73 @@ const User = require('../database/UserModel');
const bcrypt = require('bcrypt');
+exports.Login = function(req,res){
+
+ // check whether we have a session
+ if(req.session.user){
+ // Redirect to log out.
+ res.redirect("/logout");
+ }else{
+ // Render the login page.
+ res.render("pages/login",{
+ "error":"",
+ "isLoggedIn": false
+ });
+ }
+}
+
+exports.processLogin = async function(req,res){
+ // get the data.
+ let email = req.body.email;
+ let password = req.body.password;
+ // check if we have data.
+ if(email && password){
+ // check if the user exists.
+ let existingUser = await User.findOne({ where: {email:email}});
+ if(existingUser){
+ // compare the password.
+ let match = await bcrypt.compare(password,existingUser.password);
+ if(match){
+ // set the session.
+ req.session.user = existingUser.username;
+ req.session.UUID = existingUser.UUID;
+ req.session.role = existingUser.role;
+
+ // Redirect to the home page.
+ res.redirect("/");
+ }else{
+ // return an error.
+ res.render("pages/login",{
+ "error":"Invalid password",
+ isLoggedIn: false
+ });
+ }
+ }else{
+ // return an error.
+ res.render("pages/login",{
+ "error":"User with that email does not exist.",
+ isLoggedIn:false
+ });
+ }
+ }else{
+ res.status(400);
+ res.render("pages/login",{
+ "error":"Please fill in all the fields.",
+ isLoggedIn:false
+ });
+ }
+}
+
+
+exports.Logout = function(req,res){
+ // clear the session.
+ req.session.destroy();
+ // Redirect to the login page.
+ res.redirect("/login");
+}
+
+
+
exports.Register = function(req,res){
// Check whether we have a session
if(req.session.user){
diff --git a/controllers/dashboard.js b/controllers/dashboard.js
index 09aee37..4425ee6 100644
--- a/controllers/dashboard.js
+++ b/controllers/dashboard.js
@@ -1,4 +1,10 @@
const User = require('../database/UserModel');
+const { readFileSync, writeFileSync, appendFileSync, readdirSync } = require('fs');
+const { execSync } = require("child_process");
+const { siteCard } = require('../components/siteCard');
+const { containerExec } = require('../functions/system')
+
+
exports.Dashboard = async function (req, res) {
@@ -26,4 +32,216 @@ exports.Dashboard = async function (req, res) {
// Redirect to the login page
res.redirect("/login");
}
+}
+
+
+
+exports.AddSite = async function (req, res) {
+
+ let { domain, type, host, port } = req.body;
+
+ if ((req.session.role == "admin") && ( domain && type && host && port)) {
+
+
+ let { domain, type, host, port } = req.body;
+
+ // build caddyfile
+ let caddyfile = `${domain} {`
+ caddyfile += `\n\t${type} ${host}:${port}`
+ caddyfile += `\n\theader {`
+ caddyfile += `\n\t\tStrict-Transport-Security "max-age=31536000; includeSubDomains; preload"`
+ caddyfile += `\n\t}`
+ caddyfile += `\n}`
+
+
+ // save caddyfile
+ writeFileSync(`./caddyfiles/sites/${domain}.Caddyfile`, caddyfile, function (err) { console.log(err) });
+
+
+ // format 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);
+
+ req.app.locals.site_list += site;
+
+
+ res.redirect("/");
+ } else {
+ // Redirect
+ console.log('not admin or missing info')
+ res.redirect("/");
+ }
+}
+
+
+exports.RemoveSite = async function (req, res) {
+
+ if (req.session.role == "admin") {
+
+
+ for (const [key, value] of Object.entries(req.body)) {
+
+ 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`);
+ });
+
+ }
+
+ let reload = {
+ container: 'DweebProxy',
+ command: `caddy reload --config /etc/caddy/Caddyfile`
+ }
+ await containerExec(reload);
+
+
+ console.log('Removed Site(s)')
+
+ res.redirect("/refreshsites");
+ } else {
+ res.redirect("/");
+ }
+
+}
+
+
+exports.RefreshSites = async function (req, res) {
+
+ let domain, type, host, port;
+ let id = 1;
+
+ if (req.session.role == "admin") {
+
+
+ // Clear site_list.ejs
+ req.app.locals.site_list = "";
+
+
+ // check if ./caddyfiles/sites contains any .json files, then delete them
+ try {
+ let files = readdirSync('./caddyfiles/sites/');
+ files.forEach(file => {
+ if (file.includes(".json")) {
+ 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}`);
+ });
+ }
+ });
+ } catch (error) { console.log("No .json files to delete") }
+
+ // get list of Caddyfiles
+ let sites = readdirSync('./caddyfiles/sites/');
+
+
+ sites.forEach(site_name => {
+ // convert the caddyfile of each site to json
+ 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") }
+ try { type = site_file.apps.http.servers.srv0.routes[0].handle[0].routes[0].handle[1].handler } catch (error) { console.log("No Type") }
+ try { host = site_file.apps.http.servers.srv0.routes[0].handle[0].routes[0].handle[1].upstreams[0].dial.split(":")[0] } catch (error) { console.log("Not Localhost") }
+ try { port = site_file.apps.http.servers.srv0.routes[0].handle[0].routes[0].handle[1].upstreams[0].dial.split(":")[1] } catch (error) { console.log("No Port") }
+
+ // build the site card
+ let site = siteCard(type, domain, host, port, id);
+
+ // append the site card to site_list
+ req.app.locals.site_list += site;
+
+ id++;
+ });
+
+
+ res.redirect("/");
+ } else {
+ // Redirect to the login page
+ res.redirect("/");
+ }
+}
+
+
+
+exports.DisableSite = async function (req, res) {
+
+ if (req.session.role == "admin") {
+
+
+ console.log(req.body)
+ console.log('Disable Site')
+
+ res.redirect("/");
+ } else {
+ // Redirect to the login page
+ res.redirect("/login");
+ }
+}
+
+
+exports.EnableSite = async function (req, res) {
+
+ if (req.session.role == "admin") {
+
+
+ console.log(req.body)
+ console.log('Enable Site')
+
+ res.redirect("/");
+ } else {
+ // Redirect to the login page
+ res.redirect("/login");
+ }
}
\ No newline at end of file
diff --git a/controllers/login.js b/controllers/login.js
deleted file mode 100644
index f11c1ed..0000000
--- a/controllers/login.js
+++ /dev/null
@@ -1,60 +0,0 @@
-const User = require('../database/UserModel');
-const bcrypt = require('bcrypt');
-
-
-exports.Login = function(req,res){
-
- // check whether we have a session
- if(req.session.user){
- // Redirect to log out.
- res.redirect("/logout");
- }else{
- // Render the login page.
- res.render("pages/login",{
- "error":"",
- "isLoggedIn": false
- });
- }
-}
-
-exports.processLogin = async function(req,res){
- // get the data.
- let email = req.body.email;
- let password = req.body.password;
- // check if we have data.
- if(email && password){
- // check if the user exists.
- let existingUser = await User.findOne({ where: {email:email}});
- if(existingUser){
- // compare the password.
- let match = await bcrypt.compare(password,existingUser.password);
- if(match){
- // set the session.
- req.session.user = existingUser.username;
- req.session.UUID = existingUser.UUID;
- req.session.role = existingUser.role;
-
- // Redirect to the home page.
- res.redirect("/");
- }else{
- // return an error.
- res.render("pages/login",{
- "error":"Invalid password",
- isLoggedIn: false
- });
- }
- }else{
- // return an error.
- res.render("pages/login",{
- "error":"User with that email does not exist.",
- isLoggedIn:false
- });
- }
- }else{
- res.status(400);
- res.render("pages/login",{
- "error":"Please fill in all the fields.",
- isLoggedIn:false
- });
- }
-}
\ No newline at end of file
diff --git a/controllers/logout.js b/controllers/logout.js
deleted file mode 100644
index d372a13..0000000
--- a/controllers/logout.js
+++ /dev/null
@@ -1,6 +0,0 @@
-exports.Logout = function(req,res){
- // clear the session.
- req.session.destroy();
- // Redirect to the login page.
- res.redirect("/login");
-}
\ No newline at end of file
diff --git a/controllers/site_actions.js b/controllers/site_actions.js
deleted file mode 100644
index 2d569a3..0000000
--- a/controllers/site_actions.js
+++ /dev/null
@@ -1,214 +0,0 @@
-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) {
-
- let { domain, type, host, port } = req.body;
-
- if ((req.session.role == "admin") && ( domain && type && host && port)) {
-
-
- let { domain, type, host, port } = req.body;
-
- // build caddyfile
- let caddyfile = `${domain} {`
- caddyfile += `\n\t${type} ${host}:${port}`
- caddyfile += `\n\theader {`
- caddyfile += `\n\t\tStrict-Transport-Security "max-age=31536000; includeSubDomains; preload"`
- caddyfile += `\n\t}`
- caddyfile += `\n}`
-
-
- // save caddyfile
- writeFileSync(`./caddyfiles/sites/${domain}.Caddyfile`, caddyfile, function (err) { console.log(err) });
-
-
- // format 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);
-
- req.app.locals.site_list += site;
-
-
- res.redirect("/");
- } else {
- // Redirect
- console.log('not admin or missing info')
- res.redirect("/");
- }
-}
-
-
-exports.RemoveSite = async function (req, res) {
-
- if (req.session.role == "admin") {
-
-
- for (const [key, value] of Object.entries(req.body)) {
-
- 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`);
- });
-
- }
-
- let reload = {
- container: 'DweebProxy',
- command: `caddy reload --config /etc/caddy/Caddyfile`
- }
- await containerExec(reload);
-
-
- console.log('Removed Site(s)')
-
- res.redirect("/refreshsites");
- } else {
- res.redirect("/");
- }
-
-}
-
-
-exports.RefreshSites = async function (req, res) {
-
- let domain, type, host, port;
- let id = 1;
-
- if (req.session.role == "admin") {
-
-
- // Clear site_list.ejs
- req.app.locals.site_list = "";
-
-
- // check if ./caddyfiles/sites contains any .json files, then delete them
- try {
- let files = readdirSync('./caddyfiles/sites/');
- files.forEach(file => {
- if (file.includes(".json")) {
- 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}`);
- });
- }
- });
- } catch (error) { console.log("No .json files to delete") }
-
- // get list of Caddyfiles
- let sites = readdirSync('./caddyfiles/sites/');
-
-
- sites.forEach(site_name => {
- // convert the caddyfile of each site to json
- 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") }
- try { type = site_file.apps.http.servers.srv0.routes[0].handle[0].routes[0].handle[1].handler } catch (error) { console.log("No Type") }
- try { host = site_file.apps.http.servers.srv0.routes[0].handle[0].routes[0].handle[1].upstreams[0].dial.split(":")[0] } catch (error) { console.log("Not Localhost") }
- try { port = site_file.apps.http.servers.srv0.routes[0].handle[0].routes[0].handle[1].upstreams[0].dial.split(":")[1] } catch (error) { console.log("No Port") }
-
- // build the site card
- let site = siteCard(type, domain, host, port, id);
-
- // append the site card to site_list
- req.app.locals.site_list += site;
-
- id++;
- });
-
-
- res.redirect("/");
- } else {
- // Redirect to the login page
- res.redirect("/");
- }
-}
-
-
-
-exports.DisableSite = async function (req, res) {
-
- if (req.session.role == "admin") {
-
-
- console.log(req.body)
- console.log('Disable Site')
-
- res.redirect("/");
- } else {
- // Redirect to the login page
- res.redirect("/login");
- }
-}
-
-
-exports.EnableSite = async function (req, res) {
-
- if (req.session.role == "admin") {
-
-
- console.log(req.body)
- console.log('Enable Site')
-
- res.redirect("/");
- } else {
- // Redirect to the login page
- res.redirect("/login");
- }
-}
\ No newline at end of file
diff --git a/functions/compose.js b/functions/compose.js
new file mode 100644
index 0000000..153f982
--- /dev/null
+++ b/functions/compose.js
@@ -0,0 +1,205 @@
+const { writeFileSync, mkdirSync, readFileSync } = require("fs");
+const yaml = require('js-yaml');
+
+const { exec, execSync } = require("child_process");
+
+const { docker } = require('./system');
+
+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;
+ 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 ((service_name.includes('caddy')) || (name.includes('caddy'))) {
+ req.app.locals.caddy = 'enabled';
+ }
+
+ let docker_volumes = [];
+
+ 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 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}`);
+ }
+ }
+ }
+
+ // 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]`
+ }
+ }
+ }
+ }
+
+
+ // 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) });
+
+ } 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')}
+
+ }
+
+
+}
+
+
+
+module.exports.uninstall = async function (data) {
+
+
+ if (data.confirm == 'Yes') {
+
+
+ var containerName = docker.getContainer(`${data.service_name}`);
+
+ try {
+ containerName.stop(function (err, data) {
+ if (data) {
+ containerName.remove(function (err, data) {
+ });
+ }
+ });
+ } catch {
+ containerName.remove(function (err, data) {
+ });
+ }
+
+ }
+
+
+}
\ No newline at end of file
diff --git a/functions/package_manager.js b/functions/package_manager.js
index 31fe1a0..6769ac7 100644
--- a/functions/package_manager.js
+++ b/functions/package_manager.js
@@ -3,7 +3,8 @@ const yaml = require('js-yaml');
const { exec, execSync } = require("child_process");
-const { docker } = require('./system_information');
+const { docker } = require('./system');
+
var DockerodeCompose = require('dockerode-compose');
@@ -17,6 +18,11 @@ 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;
+
+ if ((service_name.includes('caddy')) || (name.includes('caddy'))) {
+ req.app.locals.caddy = 'enabled';
+ }
+
let docker_volumes = [];
if (image.startsWith('https://')){
@@ -174,26 +180,20 @@ module.exports.install = async function (data) {
module.exports.uninstall = async function (data) {
-
-
- if (data.confirm == 'Yes') {
-
-
- var containerName = docker.getContainer(`${data.service_name}`);
-
- try {
- containerName.stop(function (err, data) {
- if (data) {
- containerName.remove(function (err, data) {
- });
- }
- });
- } catch {
- containerName.remove(function (err, data) {
- });
- }
-
+ if (data.confirm == 'Yes') {
+ console.log(`Uninstalling ${data.service_name}: ${data}`);
+ var containerName = docker.getContainer(`${data.service_name}`);
+ try {
+ await containerName.stop();
+ console.log(`Stopped ${data.service_name} container`);
+ } catch {
+ console.log(`Error stopping ${data.service_name} container`);
}
-
-
+ try {
+ await containerName.remove();
+ console.log(`Removed ${data.service_name} container`);
+ } catch {
+ console.log(`Error removing ${data.service_name} container`);
+ }
+ }
}
\ No newline at end of file
diff --git a/functions/system_information.js b/functions/system.js
similarity index 86%
rename from functions/system_information.js
rename to functions/system.js
index c34498a..f34a531 100644
--- a/functions/system_information.js
+++ b/functions/system.js
@@ -2,6 +2,7 @@ const { currentLoad, mem, networkStats, fsSize, dockerContainerStats } = require
var Docker = require('dockerode');
var docker = new Docker({ socketPath: '/var/run/docker.sock' });
const { dashCard } = require('../components/dashCard');
+const { Readable } = require('stream');
// export docker
module.exports.docker = docker;
@@ -32,7 +33,7 @@ module.exports.containerList = async function () {
for (const container of data) {
- if ((container.Names[0].slice(1) != 'DweebUI') && (container.Names[0].slice(1) != 'DweebCache') && (container.Names[0].slice(1) != 'DweebProxy')) {
+ if ((container.Names[0].slice(1) != 'DweebUI') && (container.Names[0].slice(1) != 'DweebCache')) {
let imageVersion = container.Image.split('/');
let service = imageVersion[imageVersion.length - 1].split(':')[0];
@@ -167,19 +168,19 @@ module.exports.containerList = async function () {
module.exports.containerStats = async function () {
let container_stats = [];
-
const data = await docker.listContainers({ all: true });
for (const container of data) {
- if ((container.Names[0].slice(1) != 'DweebUI') && (container.Names[0].slice(1) != 'DweebCache') && (container.Names[0].slice(1) != 'DweebProxy')) {
+ if ((container.Names[0].slice(1) != 'DweebUI') && (container.Names[0].slice(1) != 'DweebCache')) {
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);
}
@@ -254,3 +255,41 @@ module.exports.containerExec = async function (data) {
+
+
+
+
+
+
+
+module.exports.containerLogs = function (data) {
+ return new Promise((resolve, reject) => {
+ let logString = '';
+
+ var options = {
+ follow: false,
+ stdout: true,
+ stderr: false,
+ timestamps: false
+ };
+
+ var containerName = docker.getContainer(data);
+
+ containerName.logs(options, function (err, stream) {
+ if (err) {
+ reject(err);
+ return;
+ }
+
+ const readableStream = Readable.from(stream);
+
+ readableStream.on('data', function (chunk) {
+ logString += chunk.toString('utf8');
+ });
+
+ readableStream.on('end', function () {
+ resolve(logString);
+ });
+ });
+ });
+};
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 58e6071..36df813 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,18 +11,16 @@
"dependencies": {
"bcrypt": "^5.1.0",
"child_process": "^1.0.2",
- "connect-redis": "^6.1.3",
"dockerode": "^4.0.0",
"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.11",
"sequelize": "^6.35.1",
"socket.io": "^4.6.1",
"sqlite3": "^5.1.6",
- "systeminformation": "^5.21.17"
+ "systeminformation": "^5.21.20"
}
},
"node_modules/@balena/dockerignore": {
@@ -79,59 +77,6 @@
"node": ">=10"
}
},
- "node_modules/@redis/bloom": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz",
- "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==",
- "peerDependencies": {
- "@redis/client": "^1.0.0"
- }
- },
- "node_modules/@redis/client": {
- "version": "1.5.12",
- "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.12.tgz",
- "integrity": "sha512-/ZjE18HRzMd80eXIIUIPcH81UoZpwulbo8FmbElrjPqH0QC0SeIKu1BOU49bO5trM5g895kAjhvalt5h77q+4A==",
- "dependencies": {
- "cluster-key-slot": "1.1.2",
- "generic-pool": "3.9.0",
- "yallist": "4.0.0"
- },
- "engines": {
- "node": ">=14"
- }
- },
- "node_modules/@redis/graph": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz",
- "integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==",
- "peerDependencies": {
- "@redis/client": "^1.0.0"
- }
- },
- "node_modules/@redis/json": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.6.tgz",
- "integrity": "sha512-rcZO3bfQbm2zPRpqo82XbW8zg4G/w4W3tI7X8Mqleq9goQjAGLL7q/1n1ZX4dXEAmORVZ4s1+uKLaUOg7LrUhw==",
- "peerDependencies": {
- "@redis/client": "^1.0.0"
- }
- },
- "node_modules/@redis/search": {
- "version": "1.1.6",
- "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.6.tgz",
- "integrity": "sha512-mZXCxbTYKBQ3M2lZnEddwEAks0Kc7nauire8q20oA0oA/LoA+E/b5Y5KZn232ztPb1FkIGqo12vh3Lf+Vw5iTw==",
- "peerDependencies": {
- "@redis/client": "^1.0.0"
- }
- },
- "node_modules/@redis/time-series": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.0.5.tgz",
- "integrity": "sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg==",
- "peerDependencies": {
- "@redis/client": "^1.0.0"
- }
- },
"node_modules/@socket.io/component-emitter": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
@@ -173,9 +118,9 @@
"integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g=="
},
"node_modules/@types/node": {
- "version": "20.9.4",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.4.tgz",
- "integrity": "sha512-wmyg8HUhcn6ACjsn8oKYjkN/zUzQeNtMy44weTJSM6p4MMzEOuKbA3OjJ267uPCOW7Xex9dyrNTful8XTQYoDA==",
+ "version": "20.10.3",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.3.tgz",
+ "integrity": "sha512-XJavIpZqiXID5Yxnxv3RUDKTN5b81ddNC3ecsA0SoFXz/QU8OGBwZGMomiq0zw+uuqbL/krztv/DINAQ/EV4gg==",
"dependencies": {
"undici-types": "~5.26.4"
}
@@ -527,14 +472,6 @@
"node": ">=6"
}
},
- "node_modules/cluster-key-slot": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
- "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@@ -564,14 +501,6 @@
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
},
- "node_modules/connect-redis": {
- "version": "6.1.3",
- "resolved": "https://registry.npmjs.org/connect-redis/-/connect-redis-6.1.3.tgz",
- "integrity": "sha512-aaNluLlAn/3JPxRwdzw7lhvEoU6Enb+d83xnokUNhC9dktqBoawKWL+WuxinxvBLTz6q9vReTnUDnUslaz74aw==",
- "engines": {
- "node": ">=12"
- }
- },
"node_modules/console-control-strings": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
@@ -1091,14 +1020,6 @@
"node": ">=10"
}
},
- "node_modules/generic-pool": {
- "version": "3.9.0",
- "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz",
- "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==",
- "engines": {
- "node": ">= 4"
- }
- },
"node_modules/get-intrinsic": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz",
@@ -1970,19 +1891,6 @@
"node": ">= 6"
}
},
- "node_modules/redis": {
- "version": "4.6.11",
- "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.11.tgz",
- "integrity": "sha512-kg1Lt4NZLYkAjPOj/WcyIGWfZfnyfKo1Wg9YKVSlzhFwxpFIl3LYI8BWy1Ab963LLDsTz2+OwdsesHKljB3WMQ==",
- "dependencies": {
- "@redis/bloom": "1.2.0",
- "@redis/client": "1.5.12",
- "@redis/graph": "1.1.1",
- "@redis/json": "1.0.6",
- "@redis/search": "1.1.6",
- "@redis/time-series": "1.0.5"
- }
- },
"node_modules/retry": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
@@ -2403,9 +2311,9 @@
}
},
"node_modules/systeminformation": {
- "version": "5.21.17",
- "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.21.17.tgz",
- "integrity": "sha512-JZYRCbIjk3WuBV59A9/rTla2rROX+aAJ9uo2Z1dI+bjieORcukClN8rlM1zE9NYKpULSbaGc+KKct/870lO0DA==",
+ "version": "5.21.20",
+ "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.21.20.tgz",
+ "integrity": "sha512-AyS1fNc+MDoAJtFknFbbo587H8h6yejJwM+H9rVusnOToIEkiMehMyD5JM7o3j55Cto20MawIZrcgNMgd4BfOQ==",
"os": [
"darwin",
"linux",
diff --git a/package.json b/package.json
index 41d539f..5c43d34 100644
--- a/package.json
+++ b/package.json
@@ -8,18 +8,16 @@
"dependencies": {
"bcrypt": "^5.1.0",
"child_process": "^1.0.2",
- "connect-redis": "^6.1.3",
"dockerode": "^4.0.0",
"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.11",
"sequelize": "^6.35.1",
"socket.io": "^4.6.1",
"sqlite3": "^5.1.6",
- "systeminformation": "^5.21.17"
+ "systeminformation": "^5.21.20"
},
"description": ""
}
diff --git a/public/js/main.js b/public/js/main.js
index 2b8797a..bf3f660 100644
--- a/public/js/main.js
+++ b/public/js/main.js
@@ -22,7 +22,7 @@ const diskBar = document.getElementById('disk-bar');
const dockerCards = document.getElementById('cards');
-// create
+const logViewer = document.getElementById('logView');
//Update usage bars
socket.on('metrics', (data) => {
@@ -115,10 +115,25 @@ function buttonAction(button) {
socket.emit('clicked', {container: button.name, state: button.id, action: button.value});
}
+
+let containerLogs;
+
function viewLogs(button) {
- console.log(`trying to view logs for ${button.name}`);
+
+ if (button.name != 'refresh') {
+ containerLogs = button.name;
+ }
+
+
+ socket.emit('logs', {container: containerLogs});
}
+socket.on('logString', (data) => {
+ logViewer.innerHTML = `
${data}
`;
+});
+
+
+
socket.on('cards', (data) => {
console.log('cards deleted');
@@ -143,10 +158,12 @@ socket.on('cards', (data) => {
});
-socket.on('container_stats', (data) => {
+socket.on('containerStats', (data) => {
let {name, cpu, ram} = data;
+ console.log(`drawing chart for ${name}`)
+
var cpu_array = JSON.parse(localStorage.getItem(`${name}_cpu`));
var ram_array = JSON.parse(localStorage.getItem(`${name}_ram`));
diff --git a/routes/index.js b/routes/index.js
index c0e37cd..30146c3 100644
--- a/routes/index.js
+++ b/routes/index.js
@@ -1,25 +1,17 @@
const express = require("express");
const router = express.Router();
-const { Dashboard } = require("../controllers/dashboard");
-
-const { AddSite, RemoveSite, RefreshSites, DisableSite, EnableSite } = require("../controllers/site_actions");
+const { Dashboard, AddSite, RemoveSite, RefreshSites, DisableSite, EnableSite } = require("../controllers/dashboard");
+const { Login, processLogin, Logout, Register, processRegister } = require("../controllers/auth");
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");
router.get("/", Dashboard);
-
-router.post("/install", Install)
-router.post("/uninstall", Uninstall)
-
router.post("/addsite", AddSite)
router.post("/removesite", RemoveSite)
router.get("/refreshsites", RefreshSites)
@@ -27,6 +19,11 @@ router.post("/disablesite", DisableSite)
router.post("/enablesite", EnableSite)
+router.post("/install", Install)
+router.post("/uninstall", Uninstall)
+
+
+
router.get("/users", Users);
router.get("/apps", Apps);
@@ -43,4 +40,7 @@ router.post("/register",processRegister); // Process Register
router.get("/logout",Logout); // Logout
+
+
+
module.exports = router;
\ No newline at end of file
diff --git a/views/pages/dashboard.ejs b/views/pages/dashboard.ejs
index 5d5cba4..f37fc34 100644
--- a/views/pages/dashboard.ejs
+++ b/views/pages/dashboard.ejs
@@ -126,6 +126,38 @@