commit
bc10e26452
30 changed files with 365 additions and 183 deletions
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -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.
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
FROM node:21-alpine
|
||||
|
||||
ENV NODE_ENV production
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
|
|
BIN
DweebUI.png
BIN
DweebUI.png
Binary file not shown.
Before Width: | Height: | Size: 114 KiB |
97
README.md
97
README.md
|
@ -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: )
|
||||
|
||||
[](https://github.com/lllllllillllllillll)
|
||||
[](https://github.com/lllllllillllllillll/DweebUI/blob/main/LICENSE)
|
||||
[](https://github.com/lllllllillllllillll)
|
||||
[](https://hub.docker.com/repository/docker/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
35
app.js
|
@ -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 => {
|
||||
|
|
|
@ -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>
|
||||
|
|
19
compose.yaml
19
compose.yaml
|
@ -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:
|
|
@ -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 = '';
|
||||
|
|
|
@ -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}});
|
||||
|
|
|
@ -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');
|
||||
|
|
47
database/ContainerSettings.js
Normal file
47
database/ContainerSettings.js
Normal 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
30
docker-compose.yaml
Normal 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
|
|
@ -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://')){
|
||||
|
|
|
@ -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
18
package-lock.json
generated
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
BIN
screenshots/logs.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 180 KiB |
|
@ -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>
|
|
@ -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"/>
|
||||
<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>
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -24,7 +24,7 @@
|
|||
</li>
|
||||
<li class="list-inline-item">
|
||||
<a href="#" class="link-secondary" rel="noopener">
|
||||
v0.07
|
||||
v0.08
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
@ -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">
|
||||
|
||||
|
|
|
@ -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>
|
Loading…
Add table
Reference in a new issue