Merge pull request #34 from lllllllillllllillll/dev

v0.08
This commit is contained in:
lllllllillllllillll 2023-12-15 01:31:20 -08:00 committed by GitHub
commit bc10e26452
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 365 additions and 183 deletions

View file

@ -1,3 +1,16 @@
## v0.08 (Dec 15th 2023)
* Updates to compose file and instructions from [steveiliop56](https://github.com/steveiliop56)
* Added SECRET field to compose file as a basic security measure.
* Visibility button to hide containers or reset view.
* Container link now uses server IP address.
* More compact container card, with style options planned.
* Improved log view.
* Removed VPN, Firewall, and VNC buttons.
* Updated dependencies (Sequelize 6.35.2)
* Fixed web pages not using the "public" static folder.
* Small tweaks to router.
* Replaced the default icon shown for missing icons (docker.png).
## v0.07 (Dec 8th 2023)
* View container logs.
* Removed Redis.

View file

@ -2,7 +2,6 @@
FROM node:21-alpine
ENV NODE_ENV production
WORKDIR /app

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

View file

@ -1,14 +1,16 @@
# DweebUI
DweebUI is a simple Docker web interface created with javascript and node.js
DweebUI is a simple Docker web interface created using Javascript, Node.JS, and Express.
Pre-Pre-Pre-Pre-Pre Alpha v0.07 ( :fire: Experimental. Don't install on any servers you care about :fire: )
Pre-Pre-Pre-Pre-Pre Alpha v0.08 ( :fire: Experimental. Don't install on any servers you care about :fire: )
[![GitHub Stars](https://img.shields.io/github/stars/lllllllillllllillll/DweebUI)](https://github.com/lllllllillllllillll)
[![GitHub License](https://img.shields.io/github/license/lllllllillllllillll/DweebUI)](https://github.com/lllllllillllllillll/DweebUI/blob/main/LICENSE)
[![GitHub Activity](https://img.shields.io/github/commit-activity/y/lllllllillllllillll/DweebUI)](https://github.com/lllllllillllllillll)
[![Docker Pulls](https://img.shields.io/docker/pulls/lllllllillllllillll/dweebui)](https://hub.docker.com/repository/docker/lllllllillllllillll/dweebui)
[![GitHub License](https://img.shields.io/github/license/lllllllillllllillll/DweebUI)](https://github.com/lllllllillllllillll/DweebUI/blob/main/LICENSE)
* I haven't used Github very much and I'm still new to javascript.
* This is the first project I've ever released and I'm sure it's full of plenty of bugs and mistakes.
* This is a personal project that I decided to share. I'm sure it has plenty of bugs and mistakes.
* I haven't used Github very much and I'm still new to Javascript.
* I probably should have waited a lot longer to share this :|
<a href="https://raw.githubusercontent.com//lllllllillllllillll/DweebUI/main/screenshots/dashboard.png"><img src="https://raw.githubusercontent.com/lllllllillllllillll/DweebUI/main/screenshots/dashboard.png" width="50%"/></a>
@ -17,49 +19,71 @@ Pre-Pre-Pre-Pre-Pre Alpha v0.07 ( :fire: Experimental. Don't install on any serv
## Features
* [x] Dashboard provides server metrics (cpu, ram, network, disk) and container controls on a single page.
* [x] Dashboard provides server metrics, container metrics, and container controls, on a single page.
* [x] View container logs.
* [ ] Update containers (planned).
* [ ] Manage your Docker networks, images, and volumes (planned).
* [x] Light/Dark Mode.
* [x] Easy to install app templates.
* [x] Automatically persists data in docker volumes if bind mount isn't used.
* [x] Proxy manager for Caddy. (Optional)
* [x] Partial Portainer Template Support (Network Mode, Ports, Volumes, Enviroment Variables, Labels, Commands, Restart Policy, Nvidia Hardware Acceleration).
* [x] Proxy manager for Caddy (Optional).
* [x] Multi-User built-in.
* [ ] User pages: Shortcuts, Requests, Support. (planned)
* [ ] User pages (planned).
* [x] Support for Windows, Linux, and MacOS.
* [ ] Import compose files. (planned)
* [x] Javascript, Node.js, and Express.
* [ ] Docker compose support (planned).
* [x] Templates.json maintains compatability with Portainer, allowing you to use the template without needing to use DweebUI.
* [ ] Manage your Docker networks, images, and volumes. (planned)
* [ ] Preset variables. (planned)
* [ ] VPN, VPS, and Firewall Toggles. (planned)
* [ ] Offline Mode. (planned)
* [x] Automatically persists data in docker volumes if bind mount isn't used.
* [ ] Preset variables (planned).
* [ ] Offline/Local Icons (planned).
## Setup
* Docker compose.yaml:
Docker Compose:
```
version: "3.9"
services:
  dweebui:
    container_name: DweebUI
    image: lllllllillllllillll/dweebui:v0.07
    environment:
      NODE_ENV: production
      PORT: 8000
      # Proxy_Manager: enabled
    restart: unless-stopped
    ports:
      - 8000:8000
    volumes:
      - dweebui:/app
      - caddyfiles:/app/caddyfiles
      - /var/run/docker.sock:/var/run/docker.sock
dweebui:
container_name: dweebui
image: lllllllillllllillll/dweebui:v0.08
# build:
# context: .
environment:
NODE_ENV: production
PORT: 8000
SECRET: MrWiskers
#Proxy_Manager: enabled
restart: unless-stopped
ports:
- 8000:8000
volumes:
- dweebui:/app
- caddyfiles:/app/caddyfiles
- /var/run/docker.sock:/var/run/docker.sock
#- ./custom-templates.json:/app/custom-templates.json
#- ./composefiles:/app/composefiles
networks:
- dweeb_network
volumes:
  dweebui:
  caddyfiles:
dweebui:
caddyfiles:
networks:
dweeb_network:
driver: bridge
```
* Using setup.sh:
Compose setup:
* Paste the above content into a file named ```docker-compose.yml``` then place it in a folder named ```dweebui```.
* Open a terminal in the ```dweebui``` folder, then enter ```docker compose up -d```.
* You may need to use ```docker-compose up -d``` or execute the command as root with either ```sudo docker compose up -d``` or ```sudo docker-compose up -d```.
Using setup.sh:
```
Extract DweebUI.zip and navigate to /DweebUI
cd DweebUI
@ -68,8 +92,9 @@ sudo ./setup.sh
```
## Credit
## Credits
* Dockerode and dockerode-compose by Apocas: https://github.com/apocas/dockerode
* UI was built using HTML and CSS elements from https://tabler.io/
* Apps template based on Portainer template provided by Lissy93: https://github.com/Lissy93/portainer-templates
* Icons from Walkxcode with some renames and additions: https://github.com/walkxcode/dashboard-icons
* Icons from Walkxcode with some renames and additions: https://github.com/walkxcode/dashboard-icons

35
app.js
View file

@ -8,10 +8,13 @@ const PORT = process.env.PORT || 8000;
const routes = require("./routes");
// Functions and variables
const { serverStats, containerList, containerStats, containerAction, containerLogs } = require('./functions/system');
const { serverStats, containerList, containerStats, containerAction, containerLogs, hiddenContainers } = require('./functions/system');
let sentList, clicked;
app.locals.site_list = '';
const Containers = require('./database/ContainerSettings');
// Configure Session
const sessionMiddleware = session({
secret: "keyboard cat",
@ -36,7 +39,7 @@ app.use([
// Start Express server
const server = app.listen(PORT, async () => {
console.log(`App listening on port ${PORT}`);
console.log(`App listening on port ${PORT}`);
});
// Start Socket.io
@ -90,11 +93,39 @@ io.on('connection', (socket) => {
clicked = false;
});
socket.on('hide', async (data) => {
console.log(`Hide ${data.container}`);
let containerExists = await Containers.findOne({ where: {name:data.container}});
if(!containerExists){
const container = await Containers.create({
name: data.container,
visibility: false
});
hiddenContainers();
console.log(`[Created] Container ${data.container} hidden`)
} else {
containerExists.update({ visibility: false });
console.log(`[Updated] Container ${data.container} hidden`)
hiddenContainers();
}
});
socket.on('reset', (data) => {
// set visibility to true for all containers
Containers.update({ visibility: true }, { where: {} });
console.log('All containers visible');
hiddenContainers();
});
// Container logs
socket.on('logs', (data) => {
containerLogs(data.container)
.then(logs => {
console.log(`Refreshed logs for ${data.container}`)
socket.emit('logString', logs);
})
.catch(err => {

View file

@ -1,11 +1,24 @@
module.exports.dashCard = function dashCard(data) {
let { name, service, id, state, image, external_port, internal_port, ports, volumes, environment_variables, labels } = data;
let { name, service, id, state, image, external_port, internal_port, ports, volumes, environment_variables, labels, IPv4, style } = data;
let margin, iconSize, fontSize = '';
if (style == "Large") {
iconSize = 'width="150px"'
} else if ((style == "Compact") || (style == undefined)) {
iconSize = 'width="100px"'
margin = 'style="margin-bottom: 0;"'
} else if (style == "Row") {
iconSize = 'width="50px"'
margin = 'style="margin-bottom: 0;"'
}
//disable controls for a docker container depending on its name
let enabled = "";
if (name.startsWith('Dweeb')) {
enabled = 'disabled=""';
let actions = "";
if (name.startsWith('dweebui')) {
actions = 'disabled=""';
}
if ( external_port == undefined ) { external_port = 0; }
@ -115,23 +128,23 @@ module.exports.dashCard = function dashCard(data) {
<div class="card">
<div class="card-body">
<div class="card-stamp card-stamp-sm">
<img heigh="150px" width="150px" src="https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/${service}.png" onerror="this.onerror=null;this.src='https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/dweebui.png';"></img>
<img ${iconSize} src="https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/${service}.png" onerror="this.onerror=null;this.src='https://raw.githubusercontent.com/lllllllillllllillll/DweebUI-Icons/main/docker.png';"></img>
</div>
<div class="d-flex align-items-center">
<div class="subheader text-yellow">${external_port}:${internal_port}</div>
<div class="ms-auto lh-1">
<div class="card-actions btn-actions">
<div class="card-actions btn-actions">
<button onclick="buttonAction(this)" name="${name}" value="start" id="${state}" class="btn-action" title="Start" ${enabled}><!-- player-play -->
<button onclick="buttonAction(this)" name="${name}" value="start" id="${state}" class="btn-action" title="Start" ${actions}><!-- player-play -->
<svg xmlns="http://www.w3.org/2000/svg" class="icon-tabler icon-tabler-player-play" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M7 4v16l13 -8z"></path></svg>
</button>
<button onclick="buttonAction(this)" name="${name}" value="stop" id="${state}" class="btn-action" title="Stop" ${enabled}><!-- player-stop -->
<button onclick="buttonAction(this)" name="${name}" value="stop" id="${state}" class="btn-action" title="Stop" ${actions}><!-- player-stop -->
<svg xmlns="http://www.w3.org/2000/svg" class="icon-tabler icon-tabler-player-stop" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M5 5m0 2a2 2 0 0 1 2 -2h10a2 2 0 0 1 2 2v10a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2 -2z"></path></svg>
</button>
<button onclick="buttonAction(this)" name="${name}" value="pause" id="${state}" class="btn-action" title="Pause" ${enabled}><!-- player-pause -->
<button onclick="buttonAction(this)" name="${name}" value="pause" id="${state}" class="btn-action" title="Pause" ${actions}><!-- player-pause -->
<svg xmlns="http://www.w3.org/2000/svg" class="icon-tabler icon-tabler-player-pause" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M6 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z"></path><path d="M14 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z"></path></svg>
</button>
<button onclick="buttonAction(this)" name="${name}" value="restart" id="${state}" class="btn-action" title="Restart" ${enabled}><!-- reload -->
<button onclick="buttonAction(this)" name="${name}" value="restart" id="${state}" class="btn-action" title="Restart" ${actions}><!-- reload -->
<svg xmlns="http://www.w3.org/2000/svg" class="icon-tabler icon-tabler-reload" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M19.933 13.041a8 8 0 1 1 -9.925 -8.788c3.899 -1 7.935 1.007 9.425 4.747"></path><path d="M20 4v5h-5"></path></svg>
</button>
<div class="dropdown">
@ -146,13 +159,22 @@ module.exports.dashCard = function dashCard(data) {
<a class="dropdown-item text-danger" data-bs-toggle="modal" data-bs-target="#${name}_modal-danger" href="#">Remove</a>
</div>
</div>
<div class="dropdown">
<a href="#" class="btn-action dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><!-- Download SVG icon from http://tabler-icons.io/i/dots-vertical -->
<svg xmlns="http://www.w3.org/2000/svg" class="icon-tabler icon-tabler-eye" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"/> <path d="M10 12a2 2 0 1 0 4 0a2 2 0 0 0 -4 0" /> <path d="M21 12c-2.4 4 -5.4 6 -9 6c-3.6 0 -6.6 -2 -9 -6c2.4 -4 5.4 -6 9 -6c3.6 0 6.6 2 9 6" /> </svg>
</a>
<div class="dropdown-menu dropdown-menu-end">
<a class="dropdown-item" onclick="hideContainer(this)" name="${name}" href="#">Hide</a>
<a class="dropdown-item" onclick="resetView()" name="${name}" href="#">Reset View</a>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="d-flex align-items-baseline">
<div class="h1 me-2" title="${name}">
<a href="http://localhost:${external_port}" target="_blank">
<div class="h1 me-2" title="${name}" ${margin}>
<a href="http://${IPv4}:${external_port}" target="_blank">
${shortened_name}
</a>
</div>

View file

@ -1,19 +0,0 @@
services:
dweebui:
container_name: DweebUI
image: lllllllillllllillll/dweebui:v0.07
environment:
NODE_ENV: production
PORT: 8000
# Proxy_Manager: enabled
restart: unless-stopped
ports:
- 8000:8000
volumes:
- dweebui:/app
- caddyfiles:/app/caddyfiles
- /var/run/docker.sock:/var/run/docker.sock
volumes:
dweebui:
caddyfiles:

View file

@ -1,11 +1,8 @@
const User = require('../database/UserModel');
const { appCard } = require('../components/appCard')
const { dashCard } = require('../components/dashCard');
const { install, uninstall } = require('../functions/package_manager');
// import { install, uninstall } from '../functions/package_manager';
const templates_json = require('../templates.json');
let templates = templates_json.templates;
@ -18,23 +15,24 @@ templates = templates.sort((a, b) => {
exports.Apps = async function(req, res) {
if (req.session.role == "admin") {
// Get the user.
let user = await User.findOne({ where: { UUID: req.session.UUID }});
let page = Number(req.query.page) || 1;
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=' + (page - 1);
let next = '/apps?page=' + (page + 1);
let prev = '/apps/' + (page - 1);
let next = '/apps/' + (page + 1);
if (page == 1) {
prev = '/apps?page=' + (page);
prev = '/apps/' + (page);
}
if (page == last_page) {
next = '/apps?page=' + (page);
next = '/apps/' + (page);
}
let apps_list = '';

View file

@ -86,11 +86,11 @@ exports.Register = function(req,res){
exports.processRegister = async function(req,res){
// Get the data.
let { first_name, last_name, username, email, password, avatar, tos } = req.body;
let { first_name, last_name, username, email, password, avatar, tos, secret } = req.body;
let role = "user";
// Check the data.
if(first_name && last_name && email && password && username && tos){
if((first_name && last_name && email && password && username && tos) && (secret == process.env.SECRET)){
// Check if there is an existing user with that username.
let existingUser = await User.findOne({ where: {username:username}});

View file

@ -1,4 +1,6 @@
const User = require('../database/UserModel');
const Containers = require('../database/ContainerSettings');
const { readFileSync, writeFileSync, appendFileSync, readdirSync } = require('fs');
const { execSync } = require("child_process");
const { siteCard } = require('../components/siteCard');

View file

@ -0,0 +1,47 @@
const { Sequelize, DataTypes } = require('sequelize');
const sequelize = new Sequelize({
dialect: 'sqlite',
storage: './database/db.sqlite',
logging: false
});
const Containers = sequelize.define('Containers', {
// Model attributes are defined here
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
name: {
type: DataTypes.STRING,
allowNull: false
},
visibility: {
type: DataTypes.STRING
// allowNull defaults to true
},
size: {
type: DataTypes.STRING
// allowNull defaults to true
},
group: {
type: DataTypes.STRING
// allowNull defaults to true
},
permissions: {
type: DataTypes.STRING
// allowNull defaults to true
}
});
async function syncModel() {
await sequelize.sync();
console.log('Containers model synced');
}
syncModel();
module.exports = Containers;

30
docker-compose.yaml Normal file
View file

@ -0,0 +1,30 @@
version: "3.9"
services:
dweebui:
container_name: dweebui
image: lllllllillllllillll/dweebui:v0.08
# build:
# context: .
environment:
PORT: 8000
SECRET: MrWiskers
#Proxy_Manager: enabled
restart: unless-stopped
ports:
- 8000:8000
volumes:
- dweebui:/app
- caddyfiles:/app/caddyfiles
- /var/run/docker.sock:/var/run/docker.sock
#- ./custom-templates.json:/app/custom-templates.json
#- ./composefiles:/app/composefiles
networks:
- dweeb_network
volumes:
dweebui:
caddyfiles:
networks:
dweeb_network:
driver: bridge

View file

@ -1,7 +1,7 @@
const { writeFileSync, mkdirSync, readFileSync } = require("fs");
const yaml = require('js-yaml');
const { exec, execSync } = require("child_process");
const { execSync } = require("child_process");
const { docker } = require('./system');
@ -18,11 +18,6 @@ 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://')){

View file

@ -1,12 +1,26 @@
const { currentLoad, mem, networkStats, fsSize, dockerContainerStats } = require('systeminformation');
const { currentLoad, mem, networkStats, fsSize, dockerContainerStats, networkInterfaces } = require('systeminformation');
var Docker = require('dockerode');
var docker = new Docker({ socketPath: '/var/run/docker.sock' });
const { dashCard } = require('../components/dashCard');
const { Readable } = require('stream');
const Containers = require('../database/ContainerSettings');
// export docker
module.exports.docker = docker;
let IPv4 = '';
networkInterfaces().then(data => {
IPv4 = data[0].ip4;
});
let hidden = '';
module.exports.hiddenContainers = async function () {
hidden = await Containers.findAll({ where: {visibility:false}});
hidden = hidden.map(a => a.name);
}
module.exports.serverStats = async function () {
const cpuUsage = await currentLoad();
const ramUsage = await mem();
@ -33,7 +47,7 @@ module.exports.containerList = async function () {
for (const container of data) {
if ((container.Names[0].slice(1) != 'DweebUI') && (container.Names[0].slice(1) != 'DweebCache')) {
if (!hidden.includes(container.Names[0].slice(1))) {
let imageVersion = container.Image.split('/');
let service = imageVersion[imageVersion.length - 1].split(':')[0];
@ -53,7 +67,9 @@ module.exports.containerList = async function () {
}
ports_list.push(ports);
}
} catch { console.log('no ports') }
} catch {
// console.log('no ports')
}
for (let i = 0; i < 12; i++) {
if (ports_list[i] == undefined) {
@ -78,7 +94,9 @@ module.exports.containerList = async function () {
readwrite: value.split(':')[2]
}
volumes_list.push(volumes);
}} catch { console.log('no volumes') }
}} catch {
// console.log('no volumes')
}
for (let i = 0; i < 12; i++) {
if (volumes_list[i] == undefined) {
let volumes = {
@ -147,6 +165,8 @@ module.exports.containerList = async function () {
volumes: volumes_list,
environment_variables: environment_variables,
labels: labels,
IPv4: IPv4,
style: "Compact"
}
let dockerCard = dashCard(container_info);
@ -172,7 +192,7 @@ module.exports.containerStats = async function () {
for (const container of data) {
if ((container.Names[0].slice(1) != 'DweebUI') && (container.Names[0].slice(1) != 'DweebCache')) {
if (!hidden.includes(container.Names[0].slice(1))) {
const stats = await dockerContainerStats(container.Id);
let container_stat = {

18
package-lock.json generated
View file

@ -1,11 +1,11 @@
{
"name": "dweeb-ui",
"name": "dweebui",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "dweeb-ui",
"name": "dweebui",
"version": "1.0.0",
"license": "ISC",
"dependencies": {
@ -17,7 +17,7 @@
"express": "^4.18.2",
"express-session": "^1.17.3",
"js-yaml": "^4.1.0",
"sequelize": "^6.35.1",
"sequelize": "^6.35.2",
"socket.io": "^4.6.1",
"sqlite3": "^5.1.6",
"systeminformation": "^5.21.20"
@ -118,9 +118,9 @@
"integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g=="
},
"node_modules/@types/node": {
"version": "20.10.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.3.tgz",
"integrity": "sha512-XJavIpZqiXID5Yxnxv3RUDKTN5b81ddNC3ecsA0SoFXz/QU8OGBwZGMomiq0zw+uuqbL/krztv/DINAQ/EV4gg==",
"version": "20.10.4",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.4.tgz",
"integrity": "sha512-D08YG6rr8X90YB56tSIuBaddy/UXAA9RKJoFvrsnogAum/0pmjkgi4+2nx96A330FmioegBWmEYQ+syqCFaveg==",
"dependencies": {
"undici-types": "~5.26.4"
}
@ -1999,9 +1999,9 @@
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
"node_modules/sequelize": {
"version": "6.35.1",
"resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.35.1.tgz",
"integrity": "sha512-UlP5k33nJsN11wCDLaWZXw9bB8w4ESKc5QmG6D04qMimwBwKVNeqRJiaaBlEJdtg8cRK+OJh95dliP+uEi+g9Q==",
"version": "6.35.2",
"resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.35.2.tgz",
"integrity": "sha512-EdzLaw2kK4/aOnWQ7ed/qh3B6/g+1DvmeXr66RwbcqSm/+QRS9X0LDI5INBibsy4eNJHWIRPo3+QK0zL+IPBHg==",
"funding": [
{
"type": "opencollective",

View file

@ -1,5 +1,5 @@
{
"name": "dweeb-ui",
"name": "dweebui",
"version": "1.0.0",
"main": "app.js",
"keywords": [],
@ -14,7 +14,7 @@
"express": "^4.18.2",
"express-session": "^1.17.3",
"js-yaml": "^4.1.0",
"sequelize": "^6.35.1",
"sequelize": "^6.35.2",
"socket.io": "^4.6.1",
"sqlite3": "^5.1.6",
"systeminformation": "^5.21.20"

View file

@ -116,6 +116,14 @@ function buttonAction(button) {
}
function hideContainer(button) {
socket.emit('hide', {container: button.name});
}
function resetView() {
socket.emit('reset');
}
let containerLogs;
function viewLogs(button) {

View file

@ -3,14 +3,14 @@ const router = express.Router();
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");
// Dashboard
router.get("/", Dashboard);
router.post("/addsite", AddSite)
router.post("/removesite", RemoveSite)
@ -18,29 +18,31 @@ router.get("/refreshsites", RefreshSites)
router.post("/disablesite", DisableSite)
router.post("/enablesite", EnableSite)
// Auth
router.get("/login",Login);
router.post("/login",processLogin);
router.get("/register", Register);
router.post("/register",processRegister);
router.get("/logout",Logout);
// Apps page
router.get("/apps", Apps);
router.get("/apps/:page", Apps);
router.get("/apps/:template/:page", Apps);
router.post("/apps", searchApps);
// Settings page
router.get("/settings", Settings);
router.get("/account", Account);
router.post("/install", Install)
router.post("/uninstall", Uninstall)
router.get("/users", Users);
router.get("/apps", Apps);
router.post("/apps", searchApps);
router.get("/settings", Settings);
router.get("/account", Account);
router.get("/login",Login); // Login page
router.post("/login",processLogin); // Process login
router.get("/register", Register); // Register page
router.post("/register",processRegister); // Process Register
router.get("/logout",Logout); // Logout
module.exports = router;

BIN
screenshots/logs.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

View file

@ -6,8 +6,8 @@
<meta http-equiv="X-UA-Compatible" content="ie=edge"/>
<title>Settings - Tabler - Premium and Open Source dashboard template with responsive and high quality UI.</title>
<!-- CSS files -->
<link href="./css/tabler.min.css?1684106062" rel="stylesheet"/>
<link href="./css/demo.min.css?1684106062" rel="stylesheet"/>
<link href="/css/tabler.min.css?1684106062" rel="stylesheet"/>
<link href="/css/demo.min.css?1684106062" rel="stylesheet"/>
<style>
@import url('https://rsms.me/inter/inter.css');
:root {
@ -19,7 +19,7 @@
</style>
</head>
<body >
<script src="./js/demo-theme.min.js?1684106062"></script>
<script src="/js/demo-theme.min.js?1684106062"></script>
<div class="page">
<!-- Navbar -->
<%- include('../partials/navbar.ejs') %>
@ -139,7 +139,7 @@
</div>
<!-- Libs JS -->
<!-- Tabler Core -->
<script src="./js/tabler.min.js?1684106062" defer></script>
<script src="./js/demo.min.js?1684106062" defer></script>
<script src="/js/tabler.min.js?1684106062" defer></script>
<script src="/js/demo.min.js?1684106062" defer></script>
</body>
</html>

View file

@ -6,8 +6,8 @@
<meta http-equiv="X-UA-Compatible" content="ie=edge"/>
<title>Apps list.</title>
<!-- CSS files -->
<link href="./css/tabler.min.css?1685973381" rel="stylesheet"/>
<link href="./css/demo.min.css?1685973381" rel="stylesheet"/>
<link href="/css/tabler.min.css?1685973381" rel="stylesheet"/>
<link href="/css/demo.min.css?1685973381" rel="stylesheet"/>
<style>
@import url('https://rsms.me/inter/inter.css');
:root {
@ -19,7 +19,7 @@
</style>
</head>
<body >
<script src="./js/demo-theme.min.js?1685973381"></script>
<script src="/js/demo-theme.min.js?1685973381"></script>
<div class="page">
<!-- Navbar -->
@ -38,13 +38,31 @@
</div>
<!-- Page title actions -->
<div class="col-auto ms-auto d-print-none">
<div class="d-flex">
<form action="/apps" id="search" name="search" method="POST">
<input type="search" class="form-control" name="search" placeholder="Search apps…"/>
<input type="search" class="form-control" name="search" placeholder="Search apps…">
</form>
 <input type="submit" form="search" class="btn btn-outline-success h-50" value="search"/>
&nbsp;<input type="submit" form="search" class="btn btn-outline-success h-50" value="search">
<div class="card-actions btn-actions">
<div class="card-actions btn-actions">
<div class="dropdown">
<a href="#" class="btn-action dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" title="Change Templates">
<svg xmlns="http://www.w3.org/2000/svg" class="icon-tabler icon-tabler-settings" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M10.325 4.317c.426 -1.756 2.924 -1.756 3.35 0a1.724 1.724 0 0 0 2.573 1.066c1.543 -.94 3.31 .826 2.37 2.37a1.724 1.724 0 0 0 1.065 2.572c1.756 .426 1.756 2.924 0 3.35a1.724 1.724 0 0 0 -1.066 2.573c.94 1.543 -.826 3.31 -2.37 2.37a1.724 1.724 0 0 0 -2.572 1.065c-.426 1.756 -2.924 1.756 -3.35 0a1.724 1.724 0 0 0 -2.573 -1.066c-1.543 .94 -3.31 -.826 -2.37 -2.37a1.724 1.724 0 0 0 -1.065 -2.572c-1.756 -.426 -1.756 -2.924 0 -3.35a1.724 1.724 0 0 0 1.066 -2.573c-.94 -1.543 .826 -3.31 2.37 -2.37c1 .608 2.296 .07 2.572 -1.065z" /><path d="M9 12a3 3 0 1 0 6 0a3 3 0 0 0 -6 0" /></svg>
</a>
<div class="dropdown-menu dropdown-menu-end">
<a class="dropdown-item" href="#">Default Template</a>
<a class="dropdown-item" href="#">Compose Files</a>
<a class="dropdown-item" href="#">Custom Template</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@ -87,7 +105,7 @@
</div>
<!-- Libs JS -->
<!-- Tabler Core -->
<script src="./js/tabler.min.js?1685973381" defer></script>
<script src="./js/demo.min.js?1685973381" defer></script>
<script src="/js/tabler.min.js?1685973381" defer></script>
<script src="/js/demo.min.js?1685973381" defer></script>
</body>
</html>

View file

@ -6,9 +6,9 @@
<meta http-equiv="X-UA-Compatible" content="ie=edge"/>
<title>Dashboard.</title>
<!-- CSS files -->
<link href="./css/tabler.min.css?1684106062" rel="stylesheet"/>
<link href="./css/demo.min.css?1684106062" rel="stylesheet"/>
<link href="./css/meters.css" rel="stylesheet"/>
<link href="/css/tabler.min.css?1684106062" rel="stylesheet"/>
<link href="/css/demo.min.css?1684106062" rel="stylesheet"/>
<link href="/css/meters.css" rel="stylesheet"/>
<style>
@import url('https://rsms.me/inter/inter.css');
:root {
@ -137,9 +137,6 @@
<div class="modal-body">
<div class="card-body">
<h4>
Log File:
</h4>
<h4>Logs:</h4>
<div id="logView">
<pre>No logs available</pre>
@ -297,13 +294,13 @@
<!-- Libs JS -->
<script src="./libs/apexcharts/dist/apexcharts.min.js?1684106062" defer></script>
<script src="/libs/apexcharts/dist/apexcharts.min.js?1684106062" defer></script>
<!-- Tabler Core -->
<script src="./js/tabler.min.js?1684106062" defer></script>
<script src="./js/demo.min.js?1684106062" defer></script>
<script src="/js/tabler.min.js?1684106062" defer></script>
<script src="/js/demo.min.js?1684106062" defer></script>
<!-- Socket.io -->
<script src="/socket.io/socket.io.js"></script>
<script src="./js/main.js"></script>
<script src="/js/main.js"></script>
</body>
</html>

View file

@ -6,8 +6,8 @@
<meta http-equiv="X-UA-Compatible" content="ie=edge"/>
<title>DweebUI - Login</title>
<!-- CSS files -->
<link href="./css/tabler.min.css?1674944402" rel="stylesheet"/>
<link href="./css/demo.min.css?1674944402" rel="stylesheet"/>
<link href="/css/tabler.min.css?1674944402" rel="stylesheet"/>
<link href="/css/demo.min.css?1674944402" rel="stylesheet"/>
<style>
@import url('https://rsms.me/inter/inter.css');
:root {
@ -19,7 +19,7 @@
</style>
</head>
<body class=" d-flex flex-column">
<script src="./js/demo-theme.js?1674944402"></script>
<script src="/js/demo-theme.js?1674944402"></script>
<div class="page page-center">
<div class="container container-tight py-4">
<div class="text-center mb-4">
@ -75,7 +75,7 @@
</div>
<!-- Libs JS -->
<!-- Tabler Core -->
<script src="./js/tabler.min.js?1674944402" defer></script>
<script src="./js/demo.min.js?1674944402" defer></script>
<script src="/js/tabler.min.js?1674944402" defer></script>
<script src="/js/demo.min.js?1674944402" defer></script>
</body>
</html>

View file

@ -6,8 +6,8 @@
<meta http-equiv="X-UA-Compatible" content="ie=edge"/>
<title>DweebUI - Register</title>
<!-- CSS files -->
<link href="./css/tabler.min.css?1684106062" rel="stylesheet"/>
<link href="./css/demo.min.css?1684106062" rel="stylesheet"/>
<link href="/css/tabler.min.css?1684106062" rel="stylesheet"/>
<link href="/css/demo.min.css?1684106062" rel="stylesheet"/>
<style>
@import url('https://rsms.me/inter/inter.css');
:root {
@ -19,7 +19,7 @@
</style>
</head>
<body class=" d-flex flex-column">
<script src="./js/demo-theme.js?1684106062"></script>
<script src="/js/demo-theme.js?1684106062"></script>
<div class="page page-center">
@ -79,6 +79,11 @@
</div>
</div>
<div class="mb-2">
<label class="form-label">SECRET</label>
<input type="text" class="form-control" id="secret" name="secret" title="Enter the value of 'SECRET' from the DweebUI docker-compose.yaml">
</div>
<label class="form-label">Avatar</label>
<div class="mb-2">
<div class="row g-2">
@ -176,7 +181,7 @@
</div>
<!-- Libs JS -->
<!-- Tabler Core -->
<script src="./js/tabler.min.js?1684106062" defer></script>
<script src="./js/demo.min.js?1684106062" defer></script>
<script src="/js/tabler.min.js?1684106062" defer></script>
<script src="/js/demo.min.js?1684106062" defer></script>
</body>
</html>

View file

@ -14,8 +14,8 @@
<meta http-equiv="X-UA-Compatible" content="ie=edge"/>
<title>Settings</title>
<!-- CSS files -->
<link href="./css/tabler.min.css?1684106062" rel="stylesheet"/>
<link href="./css/demo.min.css?1684106062" rel="stylesheet"/>
<link href="/css/tabler.min.css?1684106062" rel="stylesheet"/>
<link href="/css/demo.min.css?1684106062" rel="stylesheet"/>
<style>
@import url('https://rsms.me/inter/inter.css');
:root {
@ -27,7 +27,7 @@
</style>
</head>
<body >
<script src="./js/demo-theme.min.js?1684106062"></script>
<script src="/js/demo-theme.min.js?1684106062"></script>
<div class="page">
<!-- Navbar -->
<%- include('../partials/navbar.ejs') %>
@ -139,7 +139,7 @@
</div>
<!-- Libs JS -->
<!-- Tabler Core -->
<script src="./js/tabler.min.js?1684106062" defer></script>
<script src="./js/demo.min.js?1684106062" defer></script>
<script src="/js/tabler.min.js?1684106062" defer></script>
<script src="/js/demo.min.js?1684106062" defer></script>
</body>
</html>

View file

@ -14,8 +14,8 @@
<meta http-equiv="X-UA-Compatible" content="ie=edge"/>
<title>Users</title>
<!-- CSS files -->
<link href="./css/tabler.min.css?1685973381" rel="stylesheet"/>
<link href="./css/demo.min.css?1685973381" rel="stylesheet"/>
<link href="/css/tabler.min.css?1685973381" rel="stylesheet"/>
<link href="/css/demo.min.css?1685973381" rel="stylesheet"/>
<style>
@import url('https://rsms.me/inter/inter.css');
:root {
@ -27,7 +27,7 @@
</style>
</head>
<body >
<script src="./js/demo-theme.min.js?1685973381"></script>
<script src="/js/demo-theme.min.js?1685973381"></script>
<div class="page">
<!-- Navbar -->
@ -75,7 +75,7 @@
</div>
<!-- Libs JS -->
<!-- Tabler Core -->
<script src="./js/tabler.min.js?1685973381" defer></script>
<script src="./js/demo.min.js?1685973381" defer></script>
<script src="/js/tabler.min.js?1685973381" defer></script>
<script src="/js/demo.min.js?1685973381" defer></script>
</body>
</html>

View file

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

View file

@ -53,28 +53,29 @@
</button>
<h1 class="navbar-brand navbar-brand-autodark d-none-navbar-horizontal pe-0 pe-md-3">
<a href="#">
<img src="./static/logo.svg" width="110" height="32" alt="Tabler" class="navbar-brand-image">
<img src="/static/logo.svg" width="110" height="32" alt="Tabler" class="navbar-brand-image">
</a>
</h1>
<div class="navbar-nav flex-row order-md-last">
<div class="nav-item d-none d-md-flex me-3">
<div class="btn-list">
<!-- <div class="btn-list">
<a href="#" class="btn text-green">
<!-- Download SVG icon from http://tabler-icons.io/i/lock -->
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-lock" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M5 13a2 2 0 0 1 2 -2h10a2 2 0 0 1 2 2v6a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2 -2v-6z"></path> <path d="M11 16a1 1 0 1 0 2 0a1 1 0 0 0 -2 0"></path> <path d="M8 11v-4a4 4 0 1 1 8 0v4"></path> </svg>
VPN
</a>
<a href="#" class="btn text-green">
<!-- Download SVG icon from http://tabler-icons.io/i/shield -->
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-shield" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M12 3a12 12 0 0 0 8.5 3a12 12 0 0 1 -8.5 15a12 12 0 0 1 -8.5 -15a12 12 0 0 0 8.5 -3"></path> </svg>
Firewall
</a>
<a href="#" class="btn text-green">
<!-- Download SVG icon from http://tabler-icons.io/i/shield -->
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-screen-share" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M21 12v3a1 1 0 0 1 -1 1h-16a1 1 0 0 1 -1 -1v-10a1 1 0 0 1 1 -1h9"></path> <path d="M7 20l10 0"></path> <path d="M9 16l0 4"></path> <path d="M15 16l0 4"></path> <path d="M17 4h4v4"></path> <path d="M16 9l5 -5"></path> </svg>
VNC
</a>
</div>
</div> -->
</div>
<div class="d-none d-md-flex">

View file

@ -1,12 +0,0 @@
<tr>
<td><input class="form-check-input" type="checkbox"></td>
<td>1</td>
<td><span class="avatar me-2" style="background-image: url(./static/avatars/burns.jpg)"></span></td>
<td>John Doe</td>
<td>JDoe</td>
<td>JDoe@gmail.com</td>
<td>685468468465138</td>
<td>Admin</td>
<td><span class="badge badge-outline text-green">Active</span></td>
<td><a href="#" class="btn">Edit</a></td>
</tr>