Updated dependencies and CHANGELOG. Navbar tweaks.
This commit is contained in:
parent
e16492e462
commit
ec4746ebd3
10 changed files with 107 additions and 50 deletions
27
CHANGELOG.md
27
CHANGELOG.md
|
@ -9,15 +9,38 @@
|
||||||
* New - 'Grid view' and 'List view' button (non-functioning).
|
* New - 'Grid view' and 'List view' button (non-functioning).
|
||||||
* Added try blocks to volumes, images, and networks pages to address GitHub issues.
|
* Added try blocks to volumes, images, and networks pages to address GitHub issues.
|
||||||
* Fixed HTTPS env.
|
* Fixed HTTPS env.
|
||||||
* New - One-click sign-in if NO_AUTH env is set to 'true' and you're connecting from localhost.
|
* New - Authentication can be reduced or disabled.
|
||||||
* New (again) - PM2 to keep the app running if it encounters an error.
|
* New (again) - PM2 to keep the app running if it encounters an error.
|
||||||
* New - User registration enabled/disabled from Settings page.
|
* New - User registration enabled/disabled from Settings page.
|
||||||
* Removed 'SECRET' environment variable.
|
* Removed 'SECRET' environment variable.
|
||||||
* New - Custom container links.
|
* New - Custom container_card ports links.
|
||||||
|
* New - Custom container_card title links.
|
||||||
* Fixed issue updating view permission.
|
* Fixed issue updating view permission.
|
||||||
* Fixed issue viewing container logs.
|
* Fixed issue viewing container logs.
|
||||||
* App icons are now determined by service label instead of image name.
|
* App icons are now determined by service label instead of image name.
|
||||||
* App icons sourced from new repo with 1000+ icons.
|
* App icons sourced from new repo with 1000+ icons.
|
||||||
|
* Rewrote most of the app to use containerIDs and UUIDs universally.
|
||||||
|
* Dashboard updates now triggered by Docker events instead of constantly polling the API.
|
||||||
|
* Sessions now stored in sqlite database instead of memory.
|
||||||
|
* Updated tabler from 1.0.0-beta16 to 1.0.0-beta20.
|
||||||
|
* Updated htmx (2.0.1) and sse plugin (2.2.1).
|
||||||
|
* Seperated css and js customizations into dweebui.css and dweebui.js.
|
||||||
|
* New - Preferences page for individual user settings, like language choice.
|
||||||
|
* New - Hide username from dashboard.
|
||||||
|
* New - Footer displays version with build number.
|
||||||
|
* Updated hide container_card to be **instant**.
|
||||||
|
* Improved console.log and syslog messages.
|
||||||
|
* Fixed modal close buttons.
|
||||||
|
* Reduced amount of html being stored in js files.
|
||||||
|
* CSS and pages tweaks to make the style more consistent.
|
||||||
|
* Improved container cards to be more compact.
|
||||||
|
* Improved sponsors and credits pages.
|
||||||
|
* New - Secret supporter code.
|
||||||
|
* Fixed installs not appearing or appearing multiple times.
|
||||||
|
* Improved log view and fixed refresh button.
|
||||||
|
* Made app cards more compact.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## v0.60 (June 9th 2024) - Permissions system and import templates
|
## v0.60 (June 9th 2024) - Permissions system and import templates
|
||||||
* Converted JS template literals into HTML.
|
* Converted JS template literals into HTML.
|
||||||
|
|
20
README.md
20
README.md
|
@ -15,20 +15,20 @@
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
* [x] A dynamically updating dashboard that displays server metrics along with container metrics and container controls.
|
* [x] A dynamically updating dashboard that displays server metrics along with container metrics and container controls.
|
||||||
* [x] Multi-user support with permissions system.
|
|
||||||
* [ ] Multiple hosts (in development).
|
|
||||||
* [x] Container actions: Start, Stop, Pause, Restart, View Details, View Logs.
|
* [x] Container actions: Start, Stop, Pause, Restart, View Details, View Logs.
|
||||||
|
* [x] Multi-user support with permissions system.
|
||||||
|
* [x] Support for multiple hosts.
|
||||||
|
* [x] View and manage images, volumes, and networks.
|
||||||
* [x] Windows, Linux, and MacOS compatable.
|
* [x] Windows, Linux, and MacOS compatable.
|
||||||
* [x] Light/Dark Mode.
|
* [x] Light/Dark Mode.
|
||||||
* [x] Mobile Friendly.
|
* [x] Mobile Friendly.
|
||||||
* [x] Manage your Docker networks, images, and volumes.
|
* [x] Easy to install app templates (Compatible with Portainer).
|
||||||
* [x] Easy to install app templates.
|
* [x] Docker Compose.
|
||||||
* [x] Docker Compose Support.
|
|
||||||
* [ ] Available updates without image pull (in development).
|
* [ ] Available updates without image pull (in development).
|
||||||
* [ ] Update containers (planned).
|
* [ ] Update containers (planned).
|
||||||
* [x] Templates.json maintains compatability with Portainer, allowing you to use the template without needing to use DweebUI.
|
|
||||||
* [ ] Preset variables (planned).
|
* [ ] Preset variables (planned).
|
||||||
* [ ] Themes (planned).
|
* [ ] Themes (planned).
|
||||||
|
* [*] International language support (Languages still being updated).
|
||||||
|
|
||||||
## About
|
## About
|
||||||
|
|
||||||
|
@ -39,7 +39,13 @@
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
Docker Compose:
|
|
||||||
|
### Docker Run:
|
||||||
|
```
|
||||||
|
docker run -d --name=DweebUI -p 8000:8000 -v dweebui:/app/database -v /var/run/docker.sock:/var/run/docker.sock lllllllillllllillll/dweebui:v0.7X-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docker Compose:
|
||||||
```
|
```
|
||||||
version: "3.9"
|
version: "3.9"
|
||||||
services:
|
services:
|
||||||
|
|
|
@ -13,14 +13,8 @@ let container_link = 'http://localhost';
|
||||||
// Dashboard
|
// Dashboard
|
||||||
export const Dashboard = async function (req, res) {
|
export const Dashboard = async function (req, res) {
|
||||||
|
|
||||||
let host = req.params.host;
|
let host = req.params.host || 1;
|
||||||
req.session.host = host;
|
req.session.host = `${host}`;
|
||||||
|
|
||||||
|
|
||||||
// if (host != 1) {
|
|
||||||
// let test = await GetContainerLists(host);
|
|
||||||
// console.log(test);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Create the lists needed for the dashboard
|
// Create the lists needed for the dashboard
|
||||||
const [list, created] = await ContainerLists.findOrCreate({
|
const [list, created] = await ContainerLists.findOrCreate({
|
||||||
|
@ -36,7 +30,6 @@ export const Dashboard = async function (req, res) {
|
||||||
});
|
});
|
||||||
if (created) { console.log(`New entry created in ContainerLists for ${req.session.username}`); }
|
if (created) { console.log(`New entry created in ContainerLists for ${req.session.username}`); }
|
||||||
|
|
||||||
|
|
||||||
res.render("dashboard",{
|
res.render("dashboard",{
|
||||||
alert: '',
|
alert: '',
|
||||||
username: req.session.username,
|
username: req.session.username,
|
||||||
|
@ -252,11 +245,6 @@ async function createCard (details) {
|
||||||
container_card = container_card.replace(/StateColor/g, containerStateColor);
|
container_card = container_card.replace(/StateColor/g, containerStateColor);
|
||||||
|
|
||||||
|
|
||||||
let update_status = `<div class="text-yellow">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon-tabler icon-tabler-point-filled" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M12 7a5 5 0 1 1 -4.995 5.217l-.005 -.217l.005 -.217a5 5 0 0 1 4.995 -4.783z" stroke-width="0" fill="currentColor"></path></svg>
|
|
||||||
</div>`;
|
|
||||||
|
|
||||||
|
|
||||||
if (details.external_port == 0 && details.internal_port == 0) {
|
if (details.external_port == 0 && details.internal_port == 0) {
|
||||||
container_card = container_card.replace(/AppPorts/g, ``);
|
container_card = container_card.replace(/AppPorts/g, ``);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -50,10 +50,30 @@ export const submitRegister = async function(req,res){
|
||||||
|
|
||||||
else if (await User.findOne({ where: { [Op.or]: [{ username: username }, { email: email }] }})) {
|
else if (await User.findOne({ where: { [Op.or]: [{ username: username }, { email: email }] }})) {
|
||||||
error = "Username or email already exists";
|
error = "Username or email already exists";
|
||||||
await Syslog.create({ username: user.username, uniqueID: email, event: "Failed Registration", message: "Username or email already exists", ip: req.socket.remoteAddress });
|
await Syslog.create({ username: username, uniqueID: email, event: "Failed Registration", message: "Username or email already exists", ip: req.socket.remoteAddress });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error != '') {
|
||||||
|
|
||||||
|
let secret_input = '';
|
||||||
|
let user_registration = await ServerSettings.findOne({ where: { key: 'user_registration' }});
|
||||||
|
if (user_registration == null ) { user_registration = false; }
|
||||||
|
else { user_registration = user_registration.value; }
|
||||||
|
|
||||||
|
if (user_registration) {
|
||||||
|
secret_input = `<div class="mb-3"><label class="form-label">Secret</label>
|
||||||
|
<div class="input-group input-group-flat">
|
||||||
|
<input type="text" class="form-control" autocomplete="off" name="registration_secret">
|
||||||
|
</div>
|
||||||
|
</div>`}
|
||||||
|
|
||||||
|
res.render("register", {
|
||||||
|
"error": error,
|
||||||
|
"reg_secret": secret_input,
|
||||||
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error) { res.render("register", { "error": error }); return; }
|
|
||||||
|
|
||||||
// Returns 'admin' if no users have been created.
|
// Returns 'admin' if no users have been created.
|
||||||
async function Role() {
|
async function Role() {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { ServerSettings } from '../database/config.js';
|
import { ServerSettings } from '../database/config.js';
|
||||||
import { Alert, getLanguage, Navbar, Sidebar, Footer } from '../utils/system.js';
|
import { Alert, getLanguage, Navbar, Sidebar, Footer } from '../utils/system.js';
|
||||||
import { read, readdirSync, readFileSync } from 'fs';
|
import { read, readdirSync, readFileSync, writeFileSync } from 'fs';
|
||||||
|
|
||||||
export const Settings = async function(req,res){
|
export const Settings = async function(req,res){
|
||||||
|
|
||||||
|
@ -216,15 +216,16 @@ export const updateLanguages = async function(req,res){
|
||||||
|
|
||||||
if (language_dev != language_local) {
|
if (language_dev != language_local) {
|
||||||
console.log(`\x1b[31mLanguage: ${languages[i].language} is out of date.\x1b[0m`);
|
console.log(`\x1b[31mLanguage: ${languages[i].language} is out of date.\x1b[0m`);
|
||||||
|
console.log(`\x1b[31mUpdating ${languages[i].language}...\x1b[0m`);
|
||||||
|
writeFileSync(`./languages/${languages[i].language}`, language_dev);
|
||||||
|
console.log(`\x1b[32mLanguage: ${languages[i].language} has been updated.\x1b[0m`);
|
||||||
} else {
|
} else {
|
||||||
console.log(`\x1b[32mLanguage: ${languages[i].language} is up to date.\x1b[0m`);
|
console.log(`\x1b[32mLanguage: ${languages[i].language} is up to date.\x1b[0m`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(() => {
|
inProgress = false;
|
||||||
inProgress = false;
|
console.log('Language update complete');
|
||||||
console.log('Languages Updated');
|
|
||||||
}, 2000);
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
|
|
10
package-lock.json
generated
10
package-lock.json
generated
|
@ -1,27 +1,27 @@
|
||||||
{
|
{
|
||||||
"name": "dweebui",
|
"name": "dweebui",
|
||||||
"version": "0.70.453",
|
"version": "0.70.457",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "dweebui",
|
"name": "dweebui",
|
||||||
"version": "0.70.453",
|
"version": "0.70.457",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"adm-zip": "^0.5.15",
|
"adm-zip": "^0.5.16",
|
||||||
"bcrypt": "^5.1.1",
|
"bcrypt": "^5.1.1",
|
||||||
"connect-session-sequelize": "^7.1.7",
|
"connect-session-sequelize": "^7.1.7",
|
||||||
"dockerode": "^4.0.2",
|
"dockerode": "^4.0.2",
|
||||||
"dockerode-compose": "^1.4.0",
|
"dockerode-compose": "^1.4.0",
|
||||||
"ejs": "^3.1.10",
|
"ejs": "^3.1.10",
|
||||||
"express": "^4.19.2",
|
"express": "^4.21.0",
|
||||||
"express-session": "^1.18.0",
|
"express-session": "^1.18.0",
|
||||||
"multer": "^1.4.5-lts.1",
|
"multer": "^1.4.5-lts.1",
|
||||||
"sequelize": "^6.37.3",
|
"sequelize": "^6.37.3",
|
||||||
"sqlite3": "^5.1.7",
|
"sqlite3": "^5.1.7",
|
||||||
"systeminformation": "^5.23.5",
|
"systeminformation": "^5.23.5",
|
||||||
"yaml": "^2.5.0"
|
"yaml": "^2.5.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@balena/dockerignore": {
|
"node_modules/@balena/dockerignore": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "dweebui",
|
"name": "dweebui",
|
||||||
"version": "0.70.453",
|
"version": "0.70.457",
|
||||||
"main": "server.js",
|
"main": "server.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -12,18 +12,18 @@
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"description": "DweebUI is a WebUI for managing your containers. https://dweebui.com",
|
"description": "DweebUI is a WebUI for managing your containers. https://dweebui.com",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"adm-zip": "^0.5.15",
|
"adm-zip": "^0.5.16",
|
||||||
"bcrypt": "^5.1.1",
|
"bcrypt": "^5.1.1",
|
||||||
"connect-session-sequelize": "^7.1.7",
|
"connect-session-sequelize": "^7.1.7",
|
||||||
"dockerode": "^4.0.2",
|
"dockerode": "^4.0.2",
|
||||||
"dockerode-compose": "^1.4.0",
|
"dockerode-compose": "^1.4.0",
|
||||||
"ejs": "^3.1.10",
|
"ejs": "^3.1.10",
|
||||||
"express": "^4.19.2",
|
"express": "^4.21.0",
|
||||||
"express-session": "^1.18.0",
|
"express-session": "^1.18.0",
|
||||||
"multer": "^1.4.5-lts.1",
|
"multer": "^1.4.5-lts.1",
|
||||||
"sequelize": "^6.37.3",
|
"sequelize": "^6.37.3",
|
||||||
"sqlite3": "^5.1.7",
|
"sqlite3": "^5.1.7",
|
||||||
"systeminformation": "^5.23.5",
|
"systeminformation": "^5.23.5",
|
||||||
"yaml": "^2.5.0"
|
"yaml": "^2.5.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ export async function GetContainerLists(hostid) {
|
||||||
|
|
||||||
let host = hostid || 1;
|
let host = hostid || 1;
|
||||||
|
|
||||||
if (host == 1) {
|
if (host == 1 || host == 0) {
|
||||||
let containers = await docker.listContainers({ all: true });
|
let containers = await docker.listContainers({ all: true });
|
||||||
return containers;
|
return containers;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@ export async function Navbar (req) {
|
||||||
|
|
||||||
let username = req.session.username;
|
let username = req.session.username;
|
||||||
|
|
||||||
|
let host = '' + req.session.host;
|
||||||
|
|
||||||
let language = await getLanguage(req);
|
let language = await getLanguage(req);
|
||||||
|
|
||||||
// Check if the user wants to hide their profile name.
|
// Check if the user wants to hide their profile name.
|
||||||
|
@ -20,9 +22,11 @@ export async function Navbar (req) {
|
||||||
let sponsored = await ServerSettings.findOne({ where: { key: 'sponsored' }});
|
let sponsored = await ServerSettings.findOne({ where: { key: 'sponsored' }});
|
||||||
if (sponsored) { username = `<label class="text-yellow">${username}</label>`; }
|
if (sponsored) { username = `<label class="text-yellow">${username}</label>`; }
|
||||||
|
|
||||||
let [host2_toggle, host2_tag, host2_ip, host2_port] = ['', '', '', ''];
|
let [host0_active, host0_toggle, host0_tag, host0_ip, host0_port] = ['', '', '', '', ''];
|
||||||
let [host3_toggle, host3_tag, host3_ip, host3_port] = ['', '', '', ''];
|
let [host1_active, host1_toggle, host1_tag, host1_ip, host1_port] = ['', '', '', '', ''];
|
||||||
let [host4_toggle, host4_tag, host4_ip, host4_port] = ['', '', '', ''];
|
let [host2_active, host2_toggle, host2_tag, host2_ip, host2_port] = ['', '', '', '', ''];
|
||||||
|
let [host3_active, host3_toggle, host3_tag, host3_ip, host3_port] = ['', '', '', '', ''];
|
||||||
|
let [host4_active, host4_toggle, host4_tag, host4_ip, host4_port] = ['', '', '', '', ''];
|
||||||
|
|
||||||
const [host2, created2] = await ServerSettings.findOrCreate({ where: { key: 'host2' }, defaults: { key: 'host2', value: '' }});
|
const [host2, created2] = await ServerSettings.findOrCreate({ where: { key: 'host2' }, defaults: { key: 'host2', value: '' }});
|
||||||
const [host3, created3] = await ServerSettings.findOrCreate({ where: { key: 'host3' }, defaults: { key: 'host3', value: '' }});
|
const [host3, created3] = await ServerSettings.findOrCreate({ where: { key: 'host3' }, defaults: { key: 'host3', value: '' }});
|
||||||
|
@ -34,10 +38,17 @@ export async function Navbar (req) {
|
||||||
if (host4.value) { host4_toggle = 'checked'; [host4_tag, host4_ip, host4_port] = host4.value.split(','); }
|
if (host4.value) { host4_toggle = 'checked'; [host4_tag, host4_ip, host4_port] = host4.value.split(','); }
|
||||||
|
|
||||||
let host_buttons = '';
|
let host_buttons = '';
|
||||||
if (host2_toggle || host3_toggle || host4_toggle) { host_buttons += '<a href="/0/dashboard" class="btn" title="All">All</a> <a href="/1/dashboard" class="btn" title="Host 1">Host 1</a>'; }
|
|
||||||
if (host2_toggle) { host_buttons += `<a href="/2/dashboard" class="btn" title="${host2_tag}">${host2_tag}</a>`; }
|
if (host == '0') { host0_active = 'text-yellow'; }
|
||||||
if (host3_toggle) { host_buttons += `<a href="/3/dashboard" class="btn" title="${host3_tag}">${host3_tag}</a>`; }
|
if (host == '1') { host1_active = 'text-yellow'; }
|
||||||
if (host4_toggle) { host_buttons += `<a href="/4/dashboard" class="btn" title="${host4_tag}">${host4_tag}</a>`; }
|
if (host == '2') { host2_active = 'text-yellow'; }
|
||||||
|
if (host == '3') { host3_active = 'text-yellow'; }
|
||||||
|
if (host == '4') { host4_active = 'text-yellow'; }
|
||||||
|
|
||||||
|
if (host2_toggle || host3_toggle || host4_toggle) { host_buttons += `<a href="/0/dashboard" class="btn ${host0_active}" title="All">All</a> <a href="/1/dashboard" class="btn ${host1_active}" title="Host 1">Host 1</a>`; }
|
||||||
|
if (host2_toggle) { host_buttons += `<a href="/2/dashboard" class="btn ${host2_active}" title="${host2_tag}">${host2_tag}</a>`; }
|
||||||
|
if (host3_toggle) { host_buttons += `<a href="/3/dashboard" class="btn ${host3_active}" title="${host3_tag}">${host3_tag}</a>`; }
|
||||||
|
if (host4_toggle) { host_buttons += `<a href="/4/dashboard" class="btn ${host4_active}" title="${host4_tag}">${host4_tag}</a>`; }
|
||||||
|
|
||||||
let navbar = readFileSync('./views/partials/navbar.html', 'utf8');
|
let navbar = readFileSync('./views/partials/navbar.html', 'utf8');
|
||||||
|
|
||||||
|
@ -68,6 +79,7 @@ export async function Navbar (req) {
|
||||||
|
|
||||||
navbar = navbar.replace(/Username/g, username);
|
navbar = navbar.replace(/Username/g, username);
|
||||||
navbar = navbar.replace(/Userrole/g, req.session.role);
|
navbar = navbar.replace(/Userrole/g, req.session.role);
|
||||||
|
navbar = navbar.replace(/HostButtons/g, host_buttons);
|
||||||
return navbar;
|
return navbar;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,13 @@
|
||||||
<form class="card card-md" action="/register" method="post" autocomplete="off" novalidate>
|
<form class="card card-md" action="/register" method="post" autocomplete="off" novalidate>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h2 class="card-title text-center mb-4">Create new account</h2>
|
<h2 class="card-title text-center mb-4">Create new account</h2>
|
||||||
|
|
||||||
|
<% if(error) { %>
|
||||||
|
<div class="alert alert-danger" role="alert">
|
||||||
|
<%= error %>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label">Name</label>
|
<label class="form-label">Name</label>
|
||||||
<input type="text" class="form-control" name="name">
|
<input type="text" class="form-control" name="name">
|
||||||
|
@ -50,9 +57,9 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<% if(reg_secret) { %>
|
|
||||||
<%- reg_secret %>
|
<%- reg_secret %>
|
||||||
<% } %>
|
|
||||||
|
|
||||||
<!-- <div class="mb-3">
|
<!-- <div class="mb-3">
|
||||||
<label class="form-check">
|
<label class="form-check">
|
||||||
|
|
Loading…
Add table
Reference in a new issue