From 15722b1687d88680e06830a6960c0c8f21a76000 Mon Sep 17 00:00:00 2001 From: lllllllillllllillll Date: Mon, 22 Apr 2024 19:20:13 -0700 Subject: [PATCH] First draft of permissions system --- controllers/account.js | 2 +- controllers/apps.js | 90 +++++- controllers/dashboard.js | 434 ++++++++++++--------------- controllers/images.js | 51 ++-- controllers/login.js | 8 +- controllers/networks.js | 2 +- controllers/portal.js | 11 +- controllers/register.js | 2 +- controllers/settings.js | 2 +- controllers/supporters.js | 2 +- controllers/syslogs.js | 2 +- controllers/users.js | 5 +- controllers/volumes.js | 2 +- public/{images => img}/dweebui.svg | 0 public/{images => img}/logo.png | Bin router/index.js | 149 +++++---- server.js | 2 +- {functions => utils}/install.js | 0 {functions => utils}/uninstall.js | 0 views/apps.html | 38 ++- views/dashboard.html | 7 +- views/images.html | 2 +- views/login.html | 4 +- views/modals/import.html | 3 - views/modals/permissions.html | 2 +- views/partials/containerFull.html | 22 +- views/partials/containerSimple.html | 16 +- views/partials/navbar.html | 6 +- views/partials/user_permissions.html | 2 +- views/portal.html | 14 +- views/register.html | 4 +- 31 files changed, 433 insertions(+), 451 deletions(-) rename public/{images => img}/dweebui.svg (100%) rename public/{images => img}/logo.png (100%) rename {functions => utils}/install.js (100%) rename {functions => utils}/uninstall.js (100%) diff --git a/controllers/account.js b/controllers/account.js index 8c0ebff..ddb4c07 100644 --- a/controllers/account.js +++ b/controllers/account.js @@ -12,7 +12,7 @@ export const Account = async (req, res) => { id: user.id, email: user.email, role: user.role, - avatar: user.avatar, + avatar: req.session.user.charAt(0).toUpperCase(), alert: '', }); diff --git a/controllers/apps.js b/controllers/apps.js index 9e1fe30..360c814 100644 --- a/controllers/apps.js +++ b/controllers/apps.js @@ -1,16 +1,18 @@ -import { readFileSync } from 'fs'; +import { readFileSync, readdirSync, renameSync, mkdirSync, unlinkSync } from 'fs'; import multer from 'multer'; + const upload = multer({storage: multer.diskStorage({ destination: function (req, file, cb) { - cb(null, 'templates/') + cb(null, 'templates/tmp/') + }, + filename: function (req, file, cb) { + cb(null, file.originalname) }, - filename: function (req, file, cb) { - cb(null, file.originalname) - } }) }) + // load the default template then sort the templates by name let templatesJSON = readFileSync('./templates/templates.json'); let templates = JSON.parse(templatesJSON).templates; @@ -22,8 +24,9 @@ templates = templates.sort((a, b) => { let alert = ''; -export const Apps = (req, res) => { + +export const Apps = (req, res) => { let page = Number(req.params.page) || 1; let list_start = (page-1)*28; let list_end = (page*28); @@ -71,7 +74,64 @@ export const Apps = (req, res) => { prev: prev, next: next, apps_list: apps_list, - alert: alert || '' + alert: alert, + template_list: '', + }); + alert = ''; +} + + +export const AppTemplate = (req, res) => { + let templateTest = Number(req.params.template) || 'template.json'; + let page = Number(req.params.page) || 1; + let list_start = (page-1)*28; + let list_end = (page*28); + let last_page = Math.ceil(templates.length/28); + let prev = '/apps/' + (page-1); + let next = '/apps/' + (page+1); + if (page == 1) { prev = '/apps/' + (page); } + if (page == last_page) { next = '/apps/' + (page); } + + let apps_list = ''; + for (let i = list_start; i < list_end && i < templates.length; i++) { + let appCard = readFileSync('./views/partials/appCard.html', 'utf8'); + let name = templates[i].name || templates[i].title.toLowerCase(); + let desc = templates[i].description.slice(0, 60) + "..."; + let description = templates[i].description.replaceAll(". ", ".\n") || "no description available"; + let note = templates[i].note ? templates[i].note.replaceAll(". ", ".\n") : "no notes available"; + let image = templates[i].image; + let logo = templates[i].logo; + let categories = ''; + // set data.catagories to 'other' if data.catagories is empty or undefined + if (templates[i].categories == null || templates[i].categories == undefined || templates[i].categories == '') { + templates[i].categories = ['Other']; + } + // loop through the categories and add the badge to the card + for (let j = 0; j < templates[i].categories.length; j++) { + categories += CatagoryColor(templates[i].categories[j]); + } + appCard = appCard.replace(/AppName/g, name); + appCard = appCard.replace(/AppShortName/g, name); + appCard = appCard.replace(/AppDesc/g, desc); + appCard = appCard.replace(/AppLogo/g, logo); + appCard = appCard.replace(/AppCategories/g, categories); + apps_list += appCard; + } + // let templatesJSON = readFileSync('./templates/templates.json'); + // let templates = JSON.parse(templatesJSON).templates; + + res.render("apps", { + name: req.session.user, + role: req.session.role, + avatar: req.session.user.charAt(0).toUpperCase(), + list_start: list_start + 1, + list_end: list_end, + app_count: templates.length, + prev: prev, + next: next, + apps_list: apps_list, + alert: alert, + template_list: '', }); alert = ''; } @@ -133,7 +193,8 @@ export const appSearch = async (req, res) => { prev: prev, next: next, apps_list: apps_list, - alert: res.locals.alert || '' + alert: alert, + template_list: '', }); } @@ -399,6 +460,19 @@ export const Upload = (req, res) => { `; + let files = readdirSync('templates/tmp/'); + + for (let i = 0; i < files.length; i++) { + if (files[i].endsWith('.json')) { + renameSync(`templates/tmp/${files[i]}`, `templates/json/${files[i]}`); + } else if (files[i].endsWith('.yml') || files[i].endsWith('.yaml')) { + mkdirSync(`templates/compose/${files[i].slice(0, -4)}`); + renameSync(`templates/tmp/${files[i]}`, `templates/compose/${files[i].slice(0, -4)}/${files[i]}`); + } else { + unlinkSync(`templates/tmp/${files[i]}`); + } + } + res.redirect('/apps'); }); }; \ No newline at end of file diff --git a/controllers/dashboard.js b/controllers/dashboard.js index b72c20c..7112f3b 100644 --- a/controllers/dashboard.js +++ b/controllers/dashboard.js @@ -7,21 +7,166 @@ import { currentLoad, mem, networkStats, fsSize } from 'systeminformation'; let hidden = ''; -// The actual page +// The page export const Dashboard = (req, res) => { - let name = req.session.user; let role = req.session.role; - let avatar = name.charAt(0).toUpperCase(); - + res.render("dashboard", { name: name, - avatar: avatar, + avatar: name.charAt(0).toUpperCase(), role: role, alert: '' }); } +// The page actions +export const DashboardAction = async (req, res) => { + let name = req.header('hx-trigger-name'); + let value = req.header('hx-trigger'); + let action = req.params.action; + let modal = ''; + + switch (action) { + case 'permissions': + let title = name.charAt(0).toUpperCase() + name.slice(1); + let permissions_list = ''; + let permissions_modal = readFileSync('./views/modals/permissions.html', 'utf8'); + permissions_modal = permissions_modal.replace(/PermissionsTitle/g, title); + let users = await User.findAll({ attributes: ['username', 'UUID']}); + + for (let i = 0; i < users.length; i++) { + let user_permissions = readFileSync('./views/partials/user_permissions.html', 'utf8'); + let exists = await Permission.findOne({ where: {containerName: name, user: users[i].username}}); + + if (!exists) { + const newPermission = await Permission.create({ containerName: name, user: users[i].username, userID: users[i].UUID}); + } + + let permissions = await Permission.findOne({ where: {containerName: name, user: users[i].username}}); + if (permissions.uninstall == true) { user_permissions = user_permissions.replace(/data-UninstallCheck/g, 'checked'); } + if (permissions.edit == true) { user_permissions = user_permissions.replace(/data-EditCheck/g, 'checked'); } + if (permissions.upgrade == true) { user_permissions = user_permissions.replace(/data-UpgradeCheck/g, 'checked'); } + if (permissions.start == true) { user_permissions = user_permissions.replace(/data-StartCheck/g, 'checked'); } + if (permissions.stop == true) { user_permissions = user_permissions.replace(/data-StopCheck/g, 'checked'); } + if (permissions.pause == true) { user_permissions = user_permissions.replace(/data-PauseCheck/g, 'checked'); } + if (permissions.restart == true) { user_permissions = user_permissions.replace(/data-RestartCheck/g, 'checked'); } + if (permissions.logs == true) { user_permissions = user_permissions.replace(/data-LogsCheck/g, 'checked'); } + + user_permissions = user_permissions.replace(/EntryNumber/g, i); + user_permissions = user_permissions.replace(/EntryNumber/g, i); + user_permissions = user_permissions.replace(/EntryNumber/g, i); + user_permissions = user_permissions.replace(/PermissionsUsername/g, users[i].username); + user_permissions = user_permissions.replace(/PermissionsUsername/g, users[i].username); + user_permissions = user_permissions.replace(/PermissionsUsername/g, users[i].username); + user_permissions = user_permissions.replace(/PermissionsContainer/g, name); + user_permissions = user_permissions.replace(/PermissionsContainer/g, name); + user_permissions = user_permissions.replace(/PermissionsContainer/g, name); + + permissions_list += user_permissions; + } + + permissions_modal = permissions_modal.replace(/PermissionsList/g, permissions_list); + res.send(permissions_modal); + return; + case 'uninstall': + modal = readFileSync('./views/modals/uninstall.html', 'utf8'); + modal = modal.replace(/AppName/g, name); + // let containerPermissions = await Permission.findAll({ where: {containerName: name}}); + res.send(modal); + return; + case 'details': + modal = readFileSync('./views/modals/details.html', 'utf8'); + let details = await containerInfo(name); + modal = modal.replace(/AppName/g, details.name); + modal = modal.replace(/AppImage/g, details.image); + res.send(modal); + return; + case 'containers': + res.send(cardList); + return; + case 'updates': + res.send(newCards); + newCards = ''; + return; + case 'card': + if (hidden.includes(name) || !containersArray.find(c => c.container === name)) { + res.send(''); + return; + } else { + let details = await containerInfo(name); + let card = await createCard(details); + res.send(card); + return; + } + case 'logs': + let logString = ''; + let options = { follow: true, stdout: true, stderr: false, timestamps: false }; + docker.getContainer(name).logs(options, function (err, stream) { + if (err) { console.log(err); return; } + const readableStream = Readable.from(stream); + readableStream.on('data', function (chunk) { + logString += chunk.toString('utf8'); + }); + readableStream.on('end', function () { + res.send(`
${logString}
`); + }); + }); + return; + case 'hide': + let exists = await Container.findOne({ where: {name: name}}); + if (!exists) { + const newContainer = await Container.create({ name: name, visibility: false, }); + } else { + exists.update({ visibility: false }); + } + hidden = await Container.findAll({ where: {visibility:false}}); + hidden = hidden.map((container) => container.name); + res.send("ok"); + return; + case 'reset': + await Container.update({ visibility: true }, { where: {} }); + hidden = await Container.findAll({ where: {visibility:false}}); + hidden = hidden.map((container) => container.name); + res.send("ok"); + return; + + + } + + function status (state) { + let status = ` + + ${state} + `; + return status; + } + + // Start + if ((action == 'start') && (value == 'stopped')) { + docker.getContainer(name).start(); + res.send(status('starting')); + } else if ((action == 'start') && (value == 'paused')) { + docker.getContainer(name).unpause(); + res.send(status('starting')); + // Stop + } else if ((action == 'stop') && (value != 'stopped')) { + docker.getContainer(name).stop(); + res.send(status('stopping')); + // Pause + } else if ((action == 'pause') && (value == 'paused')) { + docker.getContainer(name).unpause(); + res.send(status('starting')); + } else if ((action == 'pause') && (value == 'running')) { + docker.getContainer(name).pause(); + res.send(status('pausing')); + // Restart + } else if (action == 'restart') { + docker.getContainer(name).restart(); + res.send(status('restarting')); + } +} + // Server metrics (CPU, RAM, TX, RX, DISK) export const Stats = async (req, res) => { let name = req.header('hx-trigger-name'); @@ -155,28 +300,6 @@ async function createCard (details) { let [ cardList, newCards, containersArray, sentArray, updatesArray ] = [ '', '', [], [], [] ]; -export async function addCard (name, state) { - console.log(`Adding card for ${name}: ${state}`); - - let details = { - name: name, - image: name, - service: name, - state: 'installing', - external_port: 0, - internal_port: 0, - ports: [], - link: 'localhost', - - } - createCard(details).then(card => { - cardList += card; - }); -} - - - - // HTMX server-side events export const SSE = async (req, res) => { res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive' }); @@ -264,242 +387,40 @@ export const Chart = async (req, res) => { res.send(chart); } -export const updateCards = async (req, res) => { - console.log('updateCards called'); - res.send(newCards); - newCards = ''; -} - - -export const Containers = async (req, res) => { - res.send(cardList); -} - -export const Card = async (req, res) => { - let name = req.header('hx-trigger-name'); - console.log(`${name} requesting updated card`); - // return nothing if in hidden or not found in containersArray - if (hidden.includes(name) || !containersArray.find(c => c.container === name)) { - res.send(''); - return; - } else { - let details = await containerInfo(name); - let card = await createCard(details); - res.send(card); - } -} - - - - -function status (state) { - let status = ` - - ${state} - `; - return status; -} - - -export const Logs = (req, res) => { - let name = req.header('hx-trigger-name'); - function containerLogs (data) { - return new Promise((resolve, reject) => { - let logString = ''; - var options = { follow: false, stdout: true, stderr: false, timestamps: false }; - var containerName = docker.getContainer(data); - containerName.logs(options, function (err, stream) { - if (err) { reject(err); return; } - const readableStream = Readable.from(stream); - readableStream.on('data', function (chunk) { - logString += chunk.toString('utf8'); - }); - readableStream.on('end', function () { - resolve(logString); - }); - }); - }); - }; - containerLogs(name).then((data) => { - res.send(`
${data}
`) - }); -} - -export const Action = async (req, res) => { - let name = req.header('hx-trigger-name'); - let state = req.header('hx-trigger'); - let action = req.params.action; - // Start - if ((action == 'start') && (state == 'stopped')) { - var containerName = docker.getContainer(name); - containerName.start(); - res.send(status('starting')); - } else if ((action == 'start') && (state == 'paused')) { - var containerName = docker.getContainer(name); - containerName.unpause(); - res.send(status('starting')); - // Stop - } else if ((action == 'stop') && (state != 'stopped')) { - var containerName = docker.getContainer(name); - containerName.stop(); - res.send(status('stopping')); - // Pause - } else if ((action == 'pause') && (state == 'paused')) { - var containerName = docker.getContainer(name); - containerName.unpause(); - res.send(status('starting')); - } else if ((action == 'pause') && (state == 'running')) { - var containerName = docker.getContainer(name); - containerName.pause(); - res.send(status('pausing')); - // Restart - } else if (action == 'restart') { - var containerName = docker.getContainer(name); - containerName.restart(); - res.send(status('restarting')); - // Hide - } else if (action == 'hide') { - let exists = await Container.findOne({ where: {name: name}}); - if (!exists) { - const newContainer = await Container.create({ name: name, visibility: false, }); - } else { - exists.update({ visibility: false }); - } - hidden = await Container.findAll({ where: {visibility:false}}); - hidden = hidden.map((container) => container.name); - res.send("ok"); - // Reset View - } else if (action == 'reset') { - await Container.update({ visibility: true }, { where: {} }); - hidden = await Container.findAll({ where: {visibility:false}}); - hidden = hidden.map((container) => container.name); - res.send("ok"); - } -} - - -export const Modals = async (req, res) => { - let name = req.header('hx-trigger-name'); - let id = req.header('hx-trigger'); - let title = name.charAt(0).toUpperCase() + name.slice(1); - - if (id == 'permissions') { - let permissions_list = ''; - let permissions_modal = readFileSync('./views/modals/permissions.html', 'utf8'); - permissions_modal = permissions_modal.replace(/PermissionsTitle/g, title); - let users = await User.findAll({ attributes: ['username', 'UUID']}); - - for (let i = 0; i < users.length; i++) { - let user_permissions = readFileSync('./views/partials/user_permissions.html', 'utf8'); - let exists = await Permission.findOne({ where: {containerName: name, user: users[i].username}}); - if (!exists) { - const newPermission = await Permission.create({ containerName: name, user: users[i].username, userID: users[i].UUID}); - } - - let permissions = await Permission.findOne({ where: {containerName: name, user: users[i].username}}); - if (permissions.uninstall == true) { user_permissions = user_permissions.replace(/data-UninstallCheck/g, 'checked'); } - if (permissions.edit == true) { user_permissions = user_permissions.replace(/data-EditCheck/g, 'checked'); } - if (permissions.upgrade == true) { user_permissions = user_permissions.replace(/data-UpgradeCheck/g, 'checked'); } - if (permissions.start == true) { user_permissions = user_permissions.replace(/data-StartCheck/g, 'checked'); } - if (permissions.stop == true) { user_permissions = user_permissions.replace(/data-StopCheck/g, 'checked'); } - if (permissions.pause == true) { user_permissions = user_permissions.replace(/data-PauseCheck/g, 'checked'); } - if (permissions.restart == true) { user_permissions = user_permissions.replace(/data-RestartCheck/g, 'checked'); } - if (permissions.logs == true) { user_permissions = user_permissions.replace(/data-LogsCheck/g, 'checked'); } - - user_permissions = user_permissions.replace(/EntryNumber/g, i); - user_permissions = user_permissions.replace(/PermissionsUsername/g, users[i].username); - user_permissions = user_permissions.replace(/PermissionsContainer/g, name); - - permissions_list += user_permissions; - } - - permissions_modal = permissions_modal.replace(/PermissionsList/g, permissions_list); - res.send(permissions_modal); - return; - } - - - - if (id == 'uninstall') { - let modal = readFileSync('./views/modals/uninstall.html', 'utf8'); - modal = modal.replace(/AppName/g, name); - // let containerPermissions = await Permission.findAll({ where: {containerName: name}}); - res.send(modal); - return; - } - - let modal = readFileSync('./views/modals/details.html', 'utf8'); - let details = await containerInfo(name); - - modal = modal.replace(/AppName/g, details.name); - modal = modal.replace(/AppImage/g, details.image); - res.send(modal); -} - - export const UpdatePermissions = async (req, res) => { - - let user = req.body.username; - let container = req.body.container; + let { user, container } = req.body; let id = req.header('hx-trigger'); - - console.log(`${req.session.user} is updating permissions for: ${user} on ${container}`); + await Permission.update({ uninstall: false, edit: false, upgrade: false, start: false, stop: false, pause: false, restart: false, logs: false }, { where: { containerName: container, user: user } }); + Object.keys(req.body).forEach(async function(key) { - if (key != 'username' && key != 'container') { + if (key != 'user' && key != 'container') { let permissions = req.body[key]; - if (permissions.includes('uninstall')){ + if (permissions.includes('uninstall')) { await Permission.update({ uninstall: true }, { where: {containerName: container, user: user}}); - } - else { - await Permission.update({ uninstall: false }, { where: {containerName: container, user: user}}); - } - - if (permissions.includes('edit')){ + } + if (permissions.includes('edit')) { await Permission.update({ edit: true }, { where: {containerName: container, user: user}}); } - else { - await Permission.update({ edit: false }, { where: {containerName: container, user: user}}); - } - - if (permissions.includes('upgrade')){ + if (permissions.includes('upgrade')) { await Permission.update({ upgrade: true }, { where: {containerName: container, user: user}}); } - else { - await Permission.update({ upgrade: false }, { where: {containerName: container, user: user}}); - } - - if (permissions.includes('start')){ + if (permissions.includes('start')) { await Permission.update({ start: true }, { where: {containerName: container, user: user}}); - } else { - await Permission.update({ start: false }, { where: {containerName: container, user: user}}); - } - - if (permissions.includes('stop')){ + } + if (permissions.includes('stop')) { await Permission.update({ stop: true }, { where: {containerName: container, user: user}}); - } else { - await Permission.update({ stop: false }, { where: {containerName: container, user: user}}); - } - - if (permissions.includes('pause')){ + } + if (permissions.includes('pause')) { await Permission.update({ pause: true }, { where: {containerName: container, user: user}}); - } else { - await Permission.update({ pause: false }, { where: {containerName: container, user: user}}); - } - - if (permissions.includes('restart')){ + } + if (permissions.includes('restart')) { await Permission.update({ restart: true }, { where: {containerName: container, user: user}}); - } else { - await Permission.update({ restart: false }, { where: {containerName: container, user: user}}); - } - - if (permissions.includes('logs')){ + } + if (permissions.includes('logs')) { await Permission.update({ logs: true }, { where: {containerName: container, user: user}}); } - else { - await Permission.update({ logs: false }, { where: {containerName: container, user: user}}); - } } }); @@ -509,8 +430,27 @@ export const UpdatePermissions = async (req, res) => { res.send(submit); return; } else if (id == 'confirmed') { - submit = ``; + submit = ``; res.send(submit); return; } +} + +// Gets imported by install.js +export async function addCard (name, state) { + console.log(`Adding card for ${name}: ${state}`); + + let details = { + name: name, + image: name, + service: name, + state: 'installing', + external_port: 0, + internal_port: 0, + ports: [], + link: 'localhost', + } + createCard(details).then(card => { + cardList += card; + }); } \ No newline at end of file diff --git a/controllers/images.js b/controllers/images.js index 34ed1ef..51af337 100644 --- a/controllers/images.js +++ b/controllers/images.js @@ -2,6 +2,31 @@ import { docker } from '../server.js'; export const Images = async function(req, res) { + let action = req.params.action; + if (action == "remove") { + console.log("Removing images"); + let images = req.body.select; + + if (typeof(images) == 'string') { + images = [images]; + } + + for (let i = 0; i < images.length; i++) { + if (images[i] != 'on') { + try { + console.log(`Removing image: ${images[i]}`); + let image = docker.getImage(images[i]); + await image.remove(); + } catch (error) { + console.log(`Unable to remove image: ${images[i]}`); + } + } + } + res.redirect("/images"); + return; + } + + let images = await docker.listImages({ all: true }); let image_list = ` @@ -48,34 +73,10 @@ export const Images = async function(req, res) { res.render("images", { name: req.session.user, role: req.session.role, - avatar: req.session.avatar, + avatar: req.session.user.charAt(0).toUpperCase(), image_list: image_list, image_count: images.length, alert: '', }); -} - - - -export const removeImage = async function(req, res) { - let images = req.body.select; - - if (typeof(images) == 'string') { - images = [images]; - } - - for (let i = 0; i < images.length; i++) { - - if (images[i] != 'on') { - try { - console.log(`Removing image: ${images[i]}`); - let image = docker.getImage(images[i]); - await image.remove(); - } catch (error) { - console.log(`Unable to remove image: ${images[i]}`); - } - } - } - res.redirect("/images"); } \ No newline at end of file diff --git a/controllers/login.js b/controllers/login.js index 223a64c..69c619c 100644 --- a/controllers/login.js +++ b/controllers/login.js @@ -43,13 +43,7 @@ export const submitLogin = async function(req,res){ message: "User logged in successfully", ip: req.socket.remoteAddress }); - - if (req.session.role == "admin") { - res.redirect("/"); - } - else { - res.redirect("/portal"); - } + res.redirect("/dashboard"); }else{ const syslog = await Syslog.create({ diff --git a/controllers/networks.js b/controllers/networks.js index 6369b2a..826674d 100644 --- a/controllers/networks.js +++ b/controllers/networks.js @@ -41,7 +41,7 @@ export const Networks = async function(req, res) { res.render("networks", { name: req.session.user, role: req.session.role, - avatar: req.session.avatar, + avatar: req.session.user.charAt(0).toUpperCase(), network_list: network_list, network_count: networks.length, alert: '', diff --git a/controllers/portal.js b/controllers/portal.js index 4e0a602..5befcfc 100644 --- a/controllers/portal.js +++ b/controllers/portal.js @@ -3,12 +3,10 @@ import { Permission, Container, User } from '../database/models.js'; import { docker } from '../server.js'; import { readFileSync } from 'fs'; - let hidden = ''; // The actual page export const Portal = (req, res) => { - let name = req.session.user; let role = req.session.role; let avatar = name.charAt(0).toUpperCase(); @@ -16,7 +14,8 @@ export const Portal = (req, res) => { res.render("portal", { name: name, avatar: avatar, - role: role + role: role, + alert: '', }); } @@ -29,12 +28,6 @@ async function CardList () { let card = await createCard(details); cardList += card; } - - // for (let i = 0; i < containers.length; i++) { - // console.log(containers[i].containerName); - // } - - } export const UserContainers = async (req, res) => { diff --git a/controllers/register.js b/controllers/register.js index a21a932..a86bc0a 100644 --- a/controllers/register.js +++ b/controllers/register.js @@ -81,7 +81,7 @@ export const submitRegister = async function(req,res){ ip: req.socket.remoteAddress }); - res.redirect("/"); + res.redirect("/dashboard"); } } catch(err) { res.render("register",{ diff --git a/controllers/settings.js b/controllers/settings.js index baaa735..67ca686 100644 --- a/controllers/settings.js +++ b/controllers/settings.js @@ -4,7 +4,7 @@ export const Settings = (req, res) => { res.render("settings", { name: req.session.user, role: req.session.role, - avatar: req.session.avatar, + avatar: req.session.user.charAt(0).toUpperCase(), alert: '', }); } \ No newline at end of file diff --git a/controllers/supporters.js b/controllers/supporters.js index c4c85e1..3d6c721 100644 --- a/controllers/supporters.js +++ b/controllers/supporters.js @@ -12,7 +12,7 @@ export const Supporters = async (req, res) => { id: user.id, email: user.email, role: user.role, - avatar: user.avatar, + avatar: req.session.user.charAt(0).toUpperCase(), alert: '', }); diff --git a/controllers/syslogs.js b/controllers/syslogs.js index 005d888..aa114f5 100644 --- a/controllers/syslogs.js +++ b/controllers/syslogs.js @@ -29,7 +29,7 @@ export const Syslogs = async function(req, res) { res.render("syslogs", { name: req.session.user || 'Dev', role: req.session.role || 'Dev', - avatar: req.session.avatar || '', + avatar: req.session.user.charAt(0).toUpperCase(), logs: logs, alert: '', }); diff --git a/controllers/users.js b/controllers/users.js index ca12f3c..2bac2bd 100644 --- a/controllers/users.js +++ b/controllers/users.js @@ -54,8 +54,9 @@ export const Users = async (req, res) => { res.render("users", { name: req.session.user, role: req.session.role, - avatar: req.session.avatar, - user_list: user_list + avatar: req.session.user.charAt(0).toUpperCase(), + user_list: user_list, + alert: '' }); } \ No newline at end of file diff --git a/controllers/volumes.js b/controllers/volumes.js index abbe1bc..7a7ac4c 100644 --- a/controllers/volumes.js +++ b/controllers/volumes.js @@ -62,7 +62,7 @@ export const Volumes = async function(req, res) { res.render("volumes", { name: req.session.user, role: req.session.role, - avatar: req.session.avatar, + avatar: req.session.user.charAt(0).toUpperCase(), volume_list: volume_list, volume_count: volumes.length, alert: '', diff --git a/public/images/dweebui.svg b/public/img/dweebui.svg similarity index 100% rename from public/images/dweebui.svg rename to public/img/dweebui.svg diff --git a/public/images/logo.png b/public/img/logo.png similarity index 100% rename from public/images/logo.png rename to public/img/logo.png diff --git a/router/index.js b/router/index.js index ce9b69e..a3b0497 100644 --- a/router/index.js +++ b/router/index.js @@ -1,15 +1,14 @@ import express from "express"; import { Permission } from '../database/models.js'; - export const router = express.Router(); // Controllers import { Login, submitLogin, Logout } from "../controllers/login.js"; import { Register, submitRegister } from "../controllers/register.js"; -import { Dashboard, Logs, Modals, Stats, Chart, SSE, Card, updateCards, Containers, Action, UpdatePermissions } from "../controllers/dashboard.js"; -import { Apps, appSearch, InstallModal, ImportModal, LearnMore, Upload } from "../controllers/apps.js"; +import { Dashboard, DashboardAction, Stats, Chart, SSE, UpdatePermissions } from "../controllers/dashboard.js"; +import { Apps, appSearch, AppTemplate, InstallModal, ImportModal, LearnMore, Upload } from "../controllers/apps.js"; import { Users } from "../controllers/users.js"; -import { Images, removeImage } from "../controllers/images.js"; +import { Images } from "../controllers/images.js"; import { Networks, removeNetwork } from "../controllers/networks.js"; import { Volumes, removeVolume } from "../controllers/volumes.js"; import { Account } from "../controllers/account.js"; @@ -17,105 +16,91 @@ import { Variables } from "../controllers/variables.js"; import { Settings } from "../controllers/settings.js"; import { Supporters, Thanks } from "../controllers/supporters.js"; import { Syslogs } from "../controllers/syslogs.js"; -import { Portal, UserContainers } from "../controllers/portal.js" +import { Install } from "../utils/install.js" +import { Uninstall } from "../utils/uninstall.js" -// Auth middleware -const auth = async (req, res, next) => { +// Permission Middleware +const adminOnly = async (req, res, next) => { + if (req.session.role == 'admin') { next(); } + else { res.redirect('/dashboard'); } +} +const sessionCheck = async (req, res, next) => { + if (req.session.user) { next(); } + else { res.redirect('/login'); } +} + +const permissionCheck = async (req, res, next) => { + if (!req.session.user) { res.redirect('/login'); return; } + else if (req.session.role == 'admin') { next(); return; } let user = req.session.user; - let role = req.session.role; - let path = req.path; - let trigger = req.header('hx-trigger-name'); - - // console.log("Auth: ", user, role, path, trigger, req.path); - - if (!user) { res.redirect('/login'); return; } - else if (role == 'admin' || path == "/portal" || path == "/account" || path == "/supporters" || path == "/thank" || path == "/user_containers") { next(); return; } - // else { res.redirect('/portal'); return; } - - let action = req.path.split("/")[2]; - - - if (action == "start" || action == "stop" || action == "pause" || action == "restart") { + let trigger = req.header('hx-trigger-name'); + const userAction = ['start', 'stop', 'restart', 'pause', 'uninstall', 'upgrade', 'edit', 'logs', 'hide', 'reset_view']; + const userPaths = ['card', 'containers', 'updates']; + if (userAction.includes(action)) { let permission = await Permission.findOne({ where: { containerName: trigger, user: user }, attributes: [`${action}`] }); - - if (permission) { + if (permission) { if (permission[action] == true) { console.log(`User ${user} has permission to ${action} ${trigger}`); next(); + return; } else { console.log(`User ${user} does not have permission to ${action} ${trigger}`); } - } else { - console.log(`No entry found for ${user} in ${trigger} permissions`); } - } - else { - res.redirect('/portal'); + } else if (userPaths.includes(action)) { + next(); + return; } } +// Utils +router.post("/install", adminOnly, Install); +router.post("/uninstall", adminOnly, Uninstall); - - -// Admin routes -router.get("/", auth, Dashboard); -router.post("/action/:action", auth, Action); -router.post("/updatePermissions", auth, UpdatePermissions); - -router.get("/logs", auth, Logs); -router.get("/modals", auth, Modals); -router.get("/stats", auth, Stats); -router.get("/chart", auth, Chart); -router.get("/sse_event", auth, SSE); -router.get("/containers", auth, Containers); -router.get("/card", auth, Card); -router.get("/new_cards", auth, updateCards); - - -router.get("/images", auth, Images); -router.post("/removeImage", auth, removeImage); - -router.get("/volumes", auth, Volumes); -router.post("/removeVolume", auth, removeVolume); - -router.get("/networks", auth, Networks); -router.post("/removeNetwork", auth, removeNetwork); - -router.get("/apps", auth, Apps); -router.get("/apps/:page", auth, Apps); -router.post("/apps", auth, appSearch); -router.get("/install_modal", auth, InstallModal) -router.get("/import_modal", auth, ImportModal) -router.get("/learn_more", auth, LearnMore) -router.post("/upload", auth, Upload); - -router.get("/users", auth, Users); -router.get("/syslogs", auth, Syslogs); - -router.get("/variables", auth, Variables); -router.get("/settings", auth, Settings); - -// User routes -router.get("/portal", auth, Portal); -router.get("/user_containers", auth, UserContainers); -router.get("/account", auth, Account); -router.get("/supporters", auth, Supporters); -router.post("/thank", auth, Thanks); - +// Routes router.get("/login", Login); router.post("/login", submitLogin); +router.get("/logout", Logout); router.get("/register", Register); router.post("/register", submitRegister); -router.get("/logout", Logout); + +router.get("/", sessionCheck, Dashboard); +router.get("/dashboard", sessionCheck, Dashboard); +router.post("/dashboard/:action", permissionCheck, DashboardAction); +router.get("/sse", sessionCheck, SSE); +router.post("/updatePermissions", adminOnly, UpdatePermissions); +router.get("/stats", sessionCheck, Stats); +router.get("/chart", sessionCheck, Chart); + +router.get("/images", adminOnly, Images); +router.post("/images/:action", adminOnly, Images); + +router.get("/volumes", adminOnly, Volumes); +router.post("/removeVolume", adminOnly, removeVolume); + +router.get("/networks", adminOnly, Networks); +router.post("/removeNetwork", adminOnly, removeNetwork); + +router.get("/apps", adminOnly, Apps); +router.get("/apps/:page", adminOnly, Apps); +router.get("/apps/template/:template", adminOnly, AppTemplate); +router.post("/apps", adminOnly, appSearch); +router.get("/install_modal", adminOnly, InstallModal) +router.get("/import_modal", adminOnly, ImportModal) +router.get("/learn_more", adminOnly, LearnMore) +router.post("/upload", adminOnly, Upload); + +router.get("/users", adminOnly, Users); +router.get("/syslogs", adminOnly, Syslogs); + +router.get("/variables", adminOnly, Variables); +router.get("/settings", adminOnly, Settings); -// Functions -import { Install } from "../functions/install.js" -import { Uninstall } from "../functions/uninstall.js" - -router.post("/install", Install); -router.post("/uninstall", Uninstall); +router.get("/account", sessionCheck, Account); +router.get("/supporters", sessionCheck, Supporters); +router.post("/thank", sessionCheck, Thanks); diff --git a/server.js b/server.js index 0eea9b4..6a4b966 100644 --- a/server.js +++ b/server.js @@ -5,7 +5,7 @@ import ejs from 'ejs'; import Docker from 'dockerode'; import { router } from './router/index.js'; import { sequelize } from './database/models.js'; -export var docker = new Docker(); +export const docker = new Docker(); const app = express(); const MemoryStore = memorystore(session); diff --git a/functions/install.js b/utils/install.js similarity index 100% rename from functions/install.js rename to utils/install.js diff --git a/functions/uninstall.js b/utils/uninstall.js similarity index 100% rename from functions/uninstall.js rename to utils/uninstall.js diff --git a/views/apps.html b/views/apps.html index 47e60fc..ffe0157 100644 --- a/views/apps.html +++ b/views/apps.html @@ -35,8 +35,7 @@
-
<%= list_start %> - <%= list_end %> of <%= app_count %> Apps
- +
<%= list_start %> - <%= list_end %> of <%= app_count %> Apps
@@ -46,27 +45,34 @@
- +
Category:
+
- +
- + + + + +
@@ -78,7 +84,7 @@
diff --git a/views/dashboard.html b/views/dashboard.html index 1101ff0..8638cf9 100644 --- a/views/dashboard.html +++ b/views/dashboard.html @@ -5,7 +5,6 @@ DweebUI - Dashboard - @@ -30,7 +29,7 @@
-
+
@@ -142,13 +141,13 @@
-
+
-
+
diff --git a/views/images.html b/views/images.html index 649e522..a667102 100644 --- a/views/images.html +++ b/views/images.html @@ -57,7 +57,7 @@ diff --git a/views/partials/containerFull.html b/views/partials/containerFull.html index 1c43941..3a39cc8 100644 --- a/views/partials/containerFull.html +++ b/views/partials/containerFull.html @@ -1,4 +1,4 @@ -
+
@@ -9,16 +9,16 @@
- - - -
diff --git a/views/partials/containerSimple.html b/views/partials/containerSimple.html index c82f0b2..ce01768 100644 --- a/views/partials/containerSimple.html +++ b/views/partials/containerSimple.html @@ -9,16 +9,16 @@
- - - - - +
diff --git a/views/portal.html b/views/portal.html index 2109e85..3416d5d 100644 --- a/views/portal.html +++ b/views/portal.html @@ -44,7 +44,6 @@
-
@@ -54,7 +53,6 @@
-
@@ -69,7 +67,6 @@
-
@@ -79,7 +76,6 @@
-
@@ -94,7 +90,6 @@
-
@@ -104,7 +99,6 @@
-
@@ -119,7 +113,6 @@
-
@@ -129,7 +122,6 @@
-
@@ -147,10 +139,10 @@
- +