diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index e4b7610..0ea7983 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,2 +1 @@ -github: [lllllllillllllillll] -patreon: DweebUI +patreon: DweebUI \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d192121..67312bf 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -15,6 +15,6 @@ updates: - package-ecosystem: "npm" directory: "/" schedule: - interval: "daily" + interval: "weekly" labels: - "🤖 Dependencies" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..591aeb8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +node_modules +database/database.sqlite +test +.dockerignore \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index be791d7..60c6b83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,20 @@ -## v0.09 (dev) +## v0.20 (dev) - The rewrite. Jumping all the way to v0.20. +* Changed to ES6 imports. +* Cleaned up file structure and code layout. +* Updated DweebUI logo. +* Visual tweaks to login and registration pages. +* Added .gitignore and .dockerignore files. +* Syslogs - View logs for sign-in and registration attempts. :new: +* Docker socket now uses default connection. +* Updated Users page displays 'inactive' if no sign-ins within 30 days. +* Dashboard updates now triggered by Docker events. +* Massive reduction in the amount of HTML, CSS, and JS on client side. +* Container graphs are significantly more efficent and no longer use localStorage. +* Made dark mode the default theme. +* Created intervals to allow application to idle or scale bettery with more users. +* Pages for images, volumes, and networks (non-functional at the moment). :new: + +## v0.09 (dev) dead. (It had so many problems that I essentially rewrote everything) * Added authentication middleware to router. * Added gzip compression. * Added PM2. diff --git a/Dockerfile b/Dockerfile index f962484..604ee4c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,7 @@ -# syntax=docker/dockerfile:1 - FROM node:21-alpine ENV NODE_ENV=production +ENV LOGGER=true WORKDIR /app diff --git a/README.md b/README.md index a9b97f1..0b97ded 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,18 @@ # DweebUI -DweebUI is a simple Docker web interface created using Javascript, Node.JS, and Express. +DweebUI is a web interface for managing Docker, with a zero-config dashboard for your containers. -Alpha v0.09 ( :fire: Experimental :fire: ) +Alpha v0.20 ( :fire: Experimental :fire: ) + +:warning: DweebUI is a management interface and should not be directly exposed to the internet:warning: +:warning: External access should be done through a VPN or secure SSH connection :warning: [![GitHub Stars](https://img.shields.io/github/stars/lllllllillllllillll/DweebUI)](https://github.com/lllllllillllllillll) [![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) +* This is a personal project that I started to get more familiar with Javascript and Node.js. +* I probably should have waited a lot longer to share this :| @@ -37,12 +42,11 @@ Docker Compose: ``` version: "3.9" services: - dweebui: container_name: dweebui - image: lllllllillllllillll/dweebui:v0.09-dev - # build: - # context: . + # image: lllllllillllllillll/dweebui:v0.20 + build: + context: . environment: NODE_ENV: production PORT: 8000 @@ -52,21 +56,15 @@ services: - 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 - + - dweebui_net volumes: dweebui: - caddyfiles: - networks: - dweeb_network: + dweebui_net: driver: bridge ``` @@ -77,14 +75,6 @@ Compose setup: * 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 -chmod +x setup.sh -sudo ./setup.sh -``` - ## Credits diff --git a/app.js b/app.js index 658c4dd..69383a0 100644 --- a/app.js +++ b/app.js @@ -1,23 +1,43 @@ -// Express -const express = require("express"); -const app = express(); -const session = require("express-session"); -const compression = require('compression'); -const helmet = require('helmet'); -const PORT = process.env.PORT || 8000; +import express from 'express'; +import session from 'express-session'; +import compression from 'compression'; +import helmet from 'helmet'; +import Docker from 'dockerode'; +import cors from 'cors'; +import { Readable } from 'stream'; +import { instrument } from '@socket.io/admin-ui' +import { router } from './router/index.js'; +import { createServer } from 'node:http'; +import { Server } from 'socket.io'; +import { sequelize, Container } from './database/models.js'; +import { currentLoad, mem, networkStats, fsSize, dockerContainerStats, dockerImages, networkInterfaces } from 'systeminformation'; +import { containerCard } from './components/containerCard.js'; -// Router -const routes = require("./routes"); +export const app = express(); +const server = createServer(app); +const port = process.env.PORT || 8000; +export var docker = new Docker(); +let [cpu, ram, tx, rx, disk] = [0, 0, 0, 0, 0]; +let [hidden, clicked, dockerEvents] = ['', false, '']; +let metricsInterval, cardsInterval, graphsInterval; +let cardList = ''; +const statsArray = {}; -// Functions and variables -const { serverStats, containerList, containerStats, containerAction, containerLogs, hiddenContainers, dockerImages, dockerVolumes, dockerNetworks } = require('./functions/system'); -let sentList, clicked; -app.locals.site_list = ''; +// socket.io admin ui +export const io = new Server(server, { + connectionStateRecovery: {}, + cors: { + origin: ['http://localhost:8000', 'https://admin.socket.io'], + methods: ['GET', 'POST'], + credentials: true + } +}); +instrument(io, { + auth: false, + readonly: true +}); -const Containers = require('./database/ContainerModel'); - - -// Configure Session +// Session middleware const sessionMiddleware = session({ secret: "keyboard cat", resave: false, @@ -26,137 +46,280 @@ const sessionMiddleware = session({ secure:false, // Only set to true if you are using HTTPS. httpOnly:false, // Only set to true if you are using HTTPS. maxAge:3600000 * 8 // Session max age in milliseconds. 3600000 = 1 hour. - } -}) + } +}); +io.engine.use(sessionMiddleware); -// Middleware +// Express middleware app.set('view engine', 'ejs'); app.use([ compression(), + cors(), helmet({contentSecurityPolicy: false}), express.static("public"), express.json(), express.urlencoded({ extended: true }), sessionMiddleware, - routes + router ]); -// Start Express server -const server = app.listen(PORT, async () => { - console.log(`App listening on port ${PORT}`); +// Initialize server +server.listen(port, () => { + async function init() { + try { + await sequelize.authenticate(); + console.log('[Connected to DB]'); + } catch (error) { + console.log('[Could not connect to DB]', error); + } + try { + await sequelize.sync(); + console.log('[Models Synced]'); + hidden = await Container.findAll({ where: {visibility:false}}); + containerCards(); + } catch (error) { + console.log('[Could not Sync Models]', error); + } + console.log(`\nServer listening on http://localhost:${port}`); + } + init(); }); -// Start Socket.io -const io = require('socket.io')(server); -io.engine.use(sessionMiddleware); +// Server metrics +let serverMetrics = async () => { + currentLoad().then(data => { + cpu = Math.round(data.currentLoad); + }); + mem().then(data => { + ram = Math.round((data.active / data.total) * 100); + }); + networkStats().then(data => { + tx = data[0].tx_bytes / (1024 * 1024); + rx = data[0].rx_bytes / (1024 * 1024); + }); + fsSize().then(data => { + disk = data[0].use; + }); +} -io.on('connection', (socket) => { +// List docker containers +let containerCards = async () => { + let list = ''; + const allContainers = await docker.listContainers({ all: true }); + for (const container of allContainers) { + if (!hidden.includes(container.Names[0].slice(1))) { - // Set user session - const user_session = socket.request.session; - console.log(`${user_session.user} connected from ${socket.handshake.headers.host} ${socket.handshake.address}`); + let imageVersion = container.Image.split('/'); + let service = imageVersion[imageVersion.length - 1].split(':')[0]; + let containerId = docker.getContainer(container.Id); + let containerInfo = await containerId.inspect(); + let ports_list = []; + try { + for (const [key, value] of Object.entries(containerInfo.HostConfig.PortBindings)) { + let ports = { + check: 'checked', + external: value[0].HostPort, + internal: key.split('/')[0], + protocol: key.split('/')[1] + } + ports_list.push(ports); + } + } catch {} - // Check if a list of containers or an install card needs to be sent - if (sentList != null) { socket.emit('cards', sentList); } - if((app.locals.install != '') && (app.locals.install != null)){ socket.emit('install', app.locals.install); } + let external_port = ports_list[0]?.external || 0; + let internal_port = ports_list[0]?.internal || 0; - - async function dockerStuff(){ - let i = await dockerImages(); - let v = await dockerVolumes(); - let n = await dockerNetworks(); - - // console.log(i, v, n); + let container_info = { + name: container.Names[0].slice(1), + service: service, + id: container.Id, + state: container.State, + image: container.Image, + external_port: external_port, + internal_port: internal_port, + ports: ports_list, + link: 'localhost', + } + let card = containerCard(container_info); + list += card; + } } + cardList = list; +} - dockerStuff(); +// Container metrics +let containerStats = async () => { + const data = await docker.listContainers({ all: true }); + for (const container of data) { + if (!hidden.includes(container.Names[0].slice(1))) { + const stats = await dockerContainerStats(container.Id); + const name = container.Names[0].slice(1); - // Send server metrics - let ServerStats = setInterval(async () => { - socket.emit('metrics', await serverStats()); - }, 1000); + if (!statsArray[name]) { + statsArray[name] = { + cpuArray: Array(15).fill(0), + ramArray: Array(15).fill(0) + }; + } + statsArray[name].cpuArray.push(Math.round(stats[0].cpuPercent)); + statsArray[name].ramArray.push(Math.round(stats[0].memPercent)); - // Send list of containers - let ContainerList = setInterval(async () => { - let cardList = await containerList(); - if (sentList !== cardList) { - sentList = cardList; - app.locals.install = ''; - socket.emit('cards', cardList); + statsArray[name].cpuArray = statsArray[name].cpuArray.slice(-15); + statsArray[name].ramArray = statsArray[name].ramArray.slice(-15); } - }, 1000); + } +} - // Send container metrics - let ContainerStats = setInterval(async () => { - let stats = await containerStats(); - for (let i = 0; i < stats.length; i++) { - socket.emit('containerStats', stats[i]); - } - }, 1000); - - // Container controls - socket.on('clicked', (data) => { - if (clicked == true) { return; } clicked = true; - let buttonPress = { - user: socket.request.session.user, - role: socket.request.session.role, - action: data.action, - container: data.container, - state: data.state - } - containerAction(buttonPress); - clicked = false; +// Store docker events +docker.getEvents((err, stream) => { + if (err) throw err; + stream.on('data', (chunk) => { + dockerEvents += chunk.toString('utf8'); }); +}); - - socket.on('hide', async (data) => { - console.log(`Hide ${data.container}`); +// Check for docker events +setInterval( () => { + if (dockerEvents != '') { + getHidden(); + containerCards(); + dockerEvents = ''; + } +}, 1000); - let containerExists = await Containers.findOne({ where: {name:data.container}}); +// Get hidden containers +async function getHidden() { + hidden = await Container.findAll({ where: {visibility:false}}); +} - if(!containerExists){ - const container = await Containers.create({ - name: data.container, - visibility: false, - }); - hiddenContainers(); - console.log(`[Created] Container ${data.container} hidden`) - - let containerData = await Containers.findOne({ where: {name:data.container}}); - console.log(containerData); - - } else { - containerExists.update({ visibility: false }); - console.log(`[Updated] Container ${data.container} hidden`) - hiddenContainers(); +// Socket.io +io.on('connection', (socket) => { + let sessionData = socket.request.session; + let sent = ''; + if (sessionData.user != undefined) { + console.log(`${sessionData.user} connected from ${socket.handshake.headers.host}`); + + // Start intervals if not already started + if (!metricsInterval) { + metricsInterval = setInterval(serverMetrics, 1000); + console.log('Metrics interval started'); + } + if (!cardsInterval) { + cardsInterval = setInterval(containerCards, 1000); + console.log('Cards interval started'); + } + if (!graphsInterval) { + graphsInterval = setInterval(containerStats, 1000); + console.log('Graphs interval started'); } - }); - socket.on('reset', (data) => { - // set visibility to true for all containers - Containers.update({ visibility: true }, { where: {} }); - console.log('All containers visible'); - hiddenContainers(); - }); + setInterval(() => { + socket.emit('metrics', [cpu, ram, tx, rx, disk]); + if (sent != cardList) { + sent = cardList; + socket.emit('containers', cardList); + } + socket.emit('containerStats', statsArray); + }, 1000); - // Container logs - socket.on('logs', (data) => { - containerLogs(data.container) - .then(logs => { - console.log(`Refreshed logs for ${data.container}`) - socket.emit('logString', logs); - }) - .catch(err => { - console.error(err); + + + socket.on('clicked', (data) => { + if (clicked == true) { return; } clicked = true; + let { name, id, value } = data; + console.log(`${sessionData.user} clicked: ${id} ${value} ${name}`); + + + if (id == 'logs'){ + function containerLogs (data) { + return new Promise((resolve, reject) => { + let logString = ''; + + var options = { + follow: false, + stdout: true, + stderr: false, + timestamps: false + }; + + var containerName = docker.getContainer(data); + + containerName.logs(options, function (err, stream) { + if (err) { + reject(err); + return; + } + + const readableStream = Readable.from(stream); + + readableStream.on('data', function (chunk) { + logString += chunk.toString('utf8'); + }); + + readableStream.on('end', function () { + resolve(logString); + }); + }); + }); + }; + containerLogs(name).then((data) => { + socket.emit('logs', data); + }).catch((err) => { + console.log(err); + }); + + } + + + if (id == 'start' || id == 'stop' || id == 'pause' || id == 'restart'){ + var containerName = docker.getContainer(name); + + if ((id == 'start') && (value == 'stopped')) { + containerName.start(); + } else if ((id == 'start') && (value == 'paused')) { + containerName.unpause(); + } else if ((id == 'stop') && (value != 'stopped')) { + containerName.stop(); + } else if ((id == 'pause') && (value == 'running')) { + containerName.pause(); + } else if ((id == 'pause') && (value == 'paused')) { + containerName.unpause(); + } else if (id == 'restart') { + containerName.restart(); + } + } + clicked = false; }); - }); - // On disconnect - socket.on('disconnect', () => { - clearInterval(ServerStats); - clearInterval(ContainerList); - clearInterval(ContainerStats); - }); + socket.on('disconnect', () => { + console.log(`${sessionData.user} disconnected`); + socket.disconnect(); + // clear intervals if no users are connected + if (io.engine.clientsCount == 0) { + clearInterval(metricsInterval); + clearInterval(cardsInterval); + clearInterval(graphsInterval); + metricsInterval = null; + cardsInterval = null; + graphsInterval = null; + console.log('All intervals cleared'); + } + }); + } else { + console.log('Missing session data'); + } +}); + + + + + + + + +// let link = ''; +// networkInterfaces().then(data => { +// link = data[0].ip4; +// }); -}); \ No newline at end of file diff --git a/caddyfiles/Caddyfile b/caddyfiles/Caddyfile deleted file mode 100644 index 7f8f696..0000000 --- a/caddyfiles/Caddyfile +++ /dev/null @@ -1 +0,0 @@ -import ./sites/* \ No newline at end of file diff --git a/components/appCard.js b/components/appCard.js index c8b2e3b..cc7377b 100644 --- a/components/appCard.js +++ b/components/appCard.js @@ -1,4 +1,4 @@ -function appCard(data) { +export const appCard = (data) => { // make data.title lowercase let app_name = data.name || data.title.toLowerCase(); @@ -227,11 +227,11 @@ function appCard(data) {
- +   Learn More - +   Install @@ -989,6 +989,4 @@ function appCard(data) {
`; -} - -module.exports = { appCard }; +} \ No newline at end of file diff --git a/components/containerCard.js b/components/containerCard.js new file mode 100644 index 0000000..590c5cb --- /dev/null +++ b/components/containerCard.js @@ -0,0 +1,118 @@ +// export for app.js +export const containerCard = (data) => { + + let { name, service, state, external_port, internal_port, ports, link } = data; + let wrapped = name; + let chart = name; + + if (name.length > 13) { + wrapped = name.slice(0, 10) + '...'; + } + + //disable controls for a docker container depending on its name + let actions = ""; + if (name.startsWith('dweebui')) { + actions = 'disabled=""'; + } + + if ( external_port == undefined ) { external_port = 0; } + if ( internal_port == undefined ) { internal_port = 0; } + + + let state_indicator = 'green'; + if (state == 'exited') { + state = 'stopped'; + state_indicator = 'red'; + } else if (state == 'paused') { + state_indicator = 'orange'; + } + + let ports_data = []; + if (ports) { + ports_data = ports; + } else { + for (let i = 0; i < 12; i++) { + + let port_check = "checked"; + let external = i; + let internal = i; + let protocol = "tcp"; + + ports_data.push({ + check: port_check, + external: external, + internal: internal, + protocol: protocol + }); + } + } + + + return ` +
+
+
+
+ +
+
+
${external_port}:${internal_port}
+
+
+
+ + + + + + +
+
+
+
+
+ +
+ + + ${state} + +
+
+
+
+
+
`; +} \ No newline at end of file diff --git a/components/dashCard.js b/components/dashCard.js deleted file mode 100644 index 4be1edf..0000000 --- a/components/dashCard.js +++ /dev/null @@ -1,1110 +0,0 @@ -module.exports.dashCard = function dashCard(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 actions = ""; - if (name.startsWith('dweebui')) { - actions = 'disabled=""'; - } - - if ( external_port == undefined ) { external_port = 0; } - if ( internal_port == undefined ) { internal_port = 0; } - - - let shortened_name = name; - if (name.length > 13) { - shortened_name = name.slice(0, 10) + '...'; - } - - let state_indicator = 'green'; - if (state == 'exited') { - state = 'stopped'; - state_indicator = 'red'; - } else if (state == 'paused') { - state_indicator = 'orange'; - } - - - let app_name = name - let modal = app_name.replaceAll(" ", "-"); - let form_id = app_name.replaceAll("-", "_"); - - let restart_policy = 'unless-stopped'; - - let ports_data = []; - if (ports) { - ports_data = ports; - } else { - for (let i = 0; i < 12; i++) { - - let port_check = "checked"; - let external = i; - let internal = i; - let protocol = "tcp"; - - ports_data.push({ - check: port_check, - external: external, - internal: internal, - protocol: protocol - }); - } - } - - let volumes_data = []; - if (volumes) { - volumes_data = volumes; - } else { - for (let i = 0; i < 12; i++) { - - let vol_check = "checked"; - let bind = i; - let container = i; - let readwrite = "rw"; - - volumes_data.push({ - check: vol_check, - bind: bind, - container: container, - readwrite: readwrite - }); - } - } - - - let env_data = []; - if (environment_variables) { - env_data = environment_variables; - } else { - for (let i = 0; i < 12; i++) { - - let env_check = "checked"; - let env_name = i; - let env_default = i; - - env_data.push({ - check: env_check, - name: env_name, - default: env_default - }); - } - } - - - let label_data = []; - if (labels) { - label_data = labels; - } else { - for (let i = 0; i < 12; i++) { - - let label_check = "checked"; - let label_name = i; - let label_default = i; - - label_data.push({ - check: label_check, - name: label_name, - value: label_default - }); - } - } - - return ` -
-
-
-
- -
-
-
${external_port}:${internal_port}
-
-
-
- - - - - - -
-
-
-
-
- -
- - - ${state} - -
-
-
-
-
-
- - - - - - - - - - - - - `; -} \ No newline at end of file diff --git a/components/siteCard.js b/components/siteCard.js deleted file mode 100644 index a12960a..0000000 --- a/components/siteCard.js +++ /dev/null @@ -1,18 +0,0 @@ -function siteCard(type, domain, host, port, id) { - - let site = `` - site += `` - site += `${id}` - site += `${domain}` - site += `${type}` - site += `${host}` - site += `${port}` - site += ` Enabled` - site += ` Enabled` - site += ` Edit ` - site += `` - - return site; -} - -module.exports = { siteCard }; \ No newline at end of file diff --git a/controllers/account.js b/controllers/account.js index c905474..4bf9195 100644 --- a/controllers/account.js +++ b/controllers/account.js @@ -1,22 +1,19 @@ -const User = require('../database/UserModel'); +import { User } from "../database/models.js"; + +export const Account = async (req, res) => { + + + let user = await User.findOne({ where: { UUID: req.session.UUID }}); + + res.render("account", { + first_name: user.name, + last_name: user.name, + name: user.name, + id: user.id, + email: user.email, + role: user.role, + avatar: user.avatar, + }); + -exports.Account = async function(req, res) { - if (req.session.user) { - // Get the user. - let user = await User.findOne({ where: { UUID: req.session.UUID }}); - // Render the home page - res.render("pages/account", { - first_name: user.first_name, - last_name: user.last_name, - name: user.first_name + ' ' + user.last_name, - id: user.id, - email: user.email, - role: user.role, - avatar: user.avatar, - isLoggedIn: true - }); - } else { - // Redirect to the login page - res.redirect("/login"); - } } diff --git a/controllers/apps.js b/controllers/apps.js index 498de7b..336246c 100644 --- a/controllers/apps.js +++ b/controllers/apps.js @@ -1,27 +1,23 @@ -const { appCard } = require('../components/appCard') -const { dashCard } = require('../components/dashCard'); -const { install, uninstall } = require('../functions/package_manager'); +import { readFileSync } from 'fs'; +import { appCard } from '../components/appCard.js'; -const templates_json = require('../templates.json'); -let templates = templates_json.templates; +let templatesJSON = readFileSync('./templates.json'); +let templates = JSON.parse(templatesJSON).templates; -// sort templates alphabetically templates = templates.sort((a, b) => { if (a.name < b.name) { return -1; } - }); - - -exports.Apps = async function(req, res) { +}); +export const Apps = (req, res) => { let page = Number(req.params.page) || 1; - let list_start = (page - 1) * 28; - let list_end = (page * 28); - let last_page = Math.ceil(templates.length / 28); + let list_start = (page-1)*28; + let list_end = (page*28); + let last_page = Math.ceil(templates.length/28); - let prev = '/apps/' + (page - 1); - let next = '/apps/' + (page + 1); + let prev = '/apps/' + (page-1); + let next = '/apps/' + (page+1); if (page == 1) { prev = '/apps/' + (page); } @@ -36,12 +32,10 @@ exports.Apps = async function(req, res) { apps_list += app_card; } - // Render the home page - res.render("pages/apps", { + res.render("apps", { name: req.session.user, role: req.session.role, avatar: req.session.avatar, - isLoggedIn: true, list_start: list_start + 1, list_end: list_end, app_count: templates.length, @@ -54,7 +48,7 @@ exports.Apps = async function(req, res) { -exports.searchApps = async function(req, res) { +export const searchApps = async (req, res) => { let page = Number(req.query.page) || 1; let list_start = (page - 1) * 28; @@ -98,12 +92,10 @@ exports.searchApps = async function(req, res) { apps_list += app_card; } - // Render the home page - res.render("pages/apps", { + res.render("apps", { name: req.session.user, role: req.session.role, avatar: req.session.avatar, - isLoggedIn: true, list_start: list_start + 1, list_end: list_end, app_count: templates.length, @@ -112,63 +104,4 @@ exports.searchApps = async function(req, res) { apps_list: apps_list }); -} - - - - - - - - - -exports.Install = async function (req, res) { - - if (req.session.role == "admin") { - - console.log(`Starting install for: ${req.body.name}`) - - install(req.body); - - let container_info = { - name: req.body.name, - service: req.body.service_name, - state: 'installing', - image: req.body.image, - restart_policy: req.body.restart_policy - } - - let installCard = dashCard(container_info); - - req.app.locals.install = installCard; - - - // Redirect to the home page - res.redirect("/"); - } else { - // Redirect to the login page - res.redirect("/login"); - } -} - - - -exports.Uninstall = async function (req, res) { - - if (req.session.role == "admin") { - - - if (req.body.confirm == 'Yes') { - - uninstall(req.body); - - } - - - // Redirect to the home page - res.redirect("/"); - } else { - // Redirect to the login page - res.redirect("/login"); - } } \ No newline at end of file diff --git a/controllers/auth.js b/controllers/auth.js deleted file mode 100644 index 6d28905..0000000 --- a/controllers/auth.js +++ /dev/null @@ -1,160 +0,0 @@ -const User = require('../database/UserModel'); -const bcrypt = require('bcrypt'); - - -exports.Login = function(req,res){ - - // check whether we have a session - if(req.session.user){ - // Redirect to log out. - res.redirect("/logout"); - }else{ - // Render the login page. - res.render("pages/login",{ - "error":"", - "isLoggedIn": false - }); - } -} - -exports.processLogin = async function(req,res){ - // get the data. - let email = req.body.email; - let password = req.body.password; - // check if we have data. - if(email && password){ - // check if the user exists. - let existingUser = await User.findOne({ where: {email:email}}); - if(existingUser){ - // compare the password. - let match = await bcrypt.compare(password,existingUser.password); - if(match){ - - // set the session. - req.session.user = existingUser.username; - req.session.UUID = existingUser.UUID; - req.session.role = existingUser.role; - req.session.avatar = existingUser.avatar; - - - // Redirect to the home page. - res.redirect("/"); - }else{ - // return an error. - res.render("pages/login",{ - "error":"Invalid password", - isLoggedIn: false - }); - } - }else{ - // return an error. - res.render("pages/login",{ - "error":"User with that email does not exist.", - isLoggedIn:false - }); - } - }else{ - res.status(400); - res.render("pages/login",{ - "error":"Please fill in all the fields.", - isLoggedIn:false - }); - } -} - - -exports.Logout = function(req,res){ - // clear the session. - req.session.destroy(); - // Redirect to the login page. - res.redirect("/login"); -} - - - -exports.Register = function(req,res){ - // Check whether we have a session - if(req.session.user){ - // Redirect to log out. - res.redirect("/logout"); - } else { - // Render the signup page. - res.render("pages/register",{ - "error":"", - isLoggedIn:false - }); - } -} - -exports.processRegister = async function(req,res){ - - // Get the data. - 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) && (secret == process.env.SECRET)){ - - // Check if there is an existing user with that username. - let existingUser = await User.findOne({ where: {username:username}}); - - let adminUser = await User.findOne({ where: {role:"admin"}}); - - if(!existingUser){ - // hash the password. - let hashedPassword = bcrypt.hashSync(password,10); - - if(!adminUser){ - console.log('Creating admin User'); - role = "admin"; - } - - try { - const user = await User.create({ - first_name: first_name, - last_name: last_name, - username: username, - email: email, - password: hashedPassword, - role: role, - group: 'all', - avatar: `` - }); - - let newUser = await User.findOne({ where: {email:email}}); - - let match = await bcrypt.compare(password,newUser.password); - if(match){ - console.log(`User session created for ${newUser.username}`) - req.session.user = newUser.username; - req.session.UUID = newUser.UUID; - req.session.role = newUser.role; - req.session.avatar = newUser.avatar; - } - - // Redirect to the home page. - res.redirect("/"); - } - catch (err) { - // return an error. - res.render("pages/register",{ - "error":"Something went wrong when creating account.", - isLoggedIn:false - }); - } - - }else{ - // return an error. - res.render("pages/register",{ - "error":"User with that username already exists.", - isLoggedIn:false - }); - } - }else{ - // Redirect to the signup page. - res.render("pages/register",{ - "error":"Please fill in all the fields and accept TOS.", - isLoggedIn:false - }); - } -} \ No newline at end of file diff --git a/controllers/dashboard.js b/controllers/dashboard.js index 5f9647c..6ba90d6 100644 --- a/controllers/dashboard.js +++ b/controllers/dashboard.js @@ -1,215 +1,24 @@ -const { readFileSync, writeFileSync, appendFileSync, readdirSync } = require('fs'); -const { execSync } = require("child_process"); -const { siteCard } = require('../components/siteCard'); -const { containerExec } = require('../functions/system') + +export const Dashboard = (req, res) => { -exports.Dashboard = async function (req, res) { - - let caddy = 'd-none'; - - if (process.env.Proxy_Manager == 'enabled') { - caddy = ''; - } - - // Render the home page - res.render("pages/dashboard", { + res.render("dashboard", { name: req.session.user, role: req.session.role, avatar: req.session.avatar, - isLoggedIn: true, - site_list: req.app.locals.site_list, - caddy: caddy }); } +export const searchDashboard = (req, res) => { + console.log(req.params); -exports.AddSite = async function (req, res) { - - let { domain, type, host, port } = req.body; - - if ( domain && type && host && port) { - - - let { domain, type, host, port } = req.body; - - // build caddyfile - let caddyfile = `${domain} {` - caddyfile += `\n\t${type} ${host}:${port}` - caddyfile += `\n\theader {` - caddyfile += `\n\t\tStrict-Transport-Security "max-age=31536000; includeSubDomains; preload"` - caddyfile += `\n\t}` - caddyfile += `\n}` - - - // save caddyfile - writeFileSync(`./caddyfiles/sites/${domain}.Caddyfile`, caddyfile, function (err) { console.log(err) }); - - - // format caddyfile - let format = { - container: 'DweebProxy', - command: `caddy fmt --overwrite /etc/caddy/sites/${domain}.Caddyfile` - } - await containerExec(format, function(err, data) { - if (err) { - console.error(err); - return; - } - console.log(`Formatted ${domain}.Caddyfile`); - }); - - ///////////////// convert caddyfile to json - let convert = { - container: 'DweebProxy', - command: `caddy adapt --config /etc/caddy/sites/${domain}.Caddyfile --pretty >> /etc/caddy/sites/${domain}.json` - } - await containerExec(convert, function(err, data) { - if (err) { - console.error(err); - return; - } - console.log(`Converted ${domain}.Caddyfile to JSON`); - }); - - ////////////// reload caddy - let reload = { - container: 'DweebProxy', - command: `caddy reload --config /etc/caddy/Caddyfile` - } - await containerExec(reload, function(err, data) { - if (err) { - console.error(err); - return; - } - console.log(`Reloaded Caddy Config`); - }); - - let site = siteCard(type, domain, host, port, 0); - - req.app.locals.site_list += site; - - - res.redirect("/"); - } else { - // Redirect - console.log('missing info') - res.redirect("/"); - } -} - - -exports.RemoveSite = async function (req, res) { - - for (const [key, value] of Object.entries(req.body)) { - - execSync(`rm ./caddyfiles/sites/${value}.Caddyfile`, (err, stdout, stderr) => { - if (err) { console.error(`error: ${err.message}`); return; } - if (stderr) { console.error(`stderr: ${stderr}`); return; } - console.log(`removed ${value}.Caddyfile`); - }); - - } - - let reload = { - container: 'DweebProxy', - command: `caddy reload --config /etc/caddy/Caddyfile` - } - await containerExec(reload); - - - console.log('Removed Site(s)') - - res.redirect("/refreshsites"); - - -} - - -exports.RefreshSites = async function (req, res) { - - let domain, type, host, port; - let id = 1; - - // Clear site_list.ejs - req.app.locals.site_list = ""; - - - // check if ./caddyfiles/sites contains any .json files, then delete them - try { - let files = readdirSync('./caddyfiles/sites/'); - files.forEach(file => { - if (file.includes(".json")) { - execSync(`rm ./caddyfiles/sites/${file}`, (err, stdout, stderr) => { - if (err) { console.error(`error: ${err.message}`); return; } - if (stderr) { console.error(`stderr: ${stderr}`); return; } - console.log(`removed ${file}`); - }); - } - }); - } catch (error) { console.log("No .json files to delete") } - - // get list of Caddyfiles - let sites = readdirSync('./caddyfiles/sites/'); - - - sites.forEach(site_name => { - // convert the caddyfile of each site to json - let convert = { - container: 'DweebProxy', - command: `caddy adapt --config ./caddyfiles/sites/${site_name} --pretty >> ./caddyfiles/sites/${site_name}.json` - } - containerExec(convert); - - try { - // read the json file - let site_file = readFileSync(`./caddyfiles/sites/${site_name}.json`, 'utf8'); - // fix whitespace and parse the json file - site_file = site_file.replace(/ /g, " "); - site_file = JSON.parse(site_file); - } catch (error) { console.log("No .json file to read") } - - - // get the domain, type, host, and port from the json file - try { domain = site_file.apps.http.servers.srv0.routes[0].match[0].host[0] } catch (error) { console.log("No Domain") } - try { type = site_file.apps.http.servers.srv0.routes[0].handle[0].routes[0].handle[1].handler } catch (error) { console.log("No Type") } - try { host = site_file.apps.http.servers.srv0.routes[0].handle[0].routes[0].handle[1].upstreams[0].dial.split(":")[0] } catch (error) { console.log("Not Localhost") } - try { port = site_file.apps.http.servers.srv0.routes[0].handle[0].routes[0].handle[1].upstreams[0].dial.split(":")[1] } catch (error) { console.log("No Port") } - - // build the site card - let site = siteCard(type, domain, host, port, id); - - // append the site card to site_list - req.app.locals.site_list += site; - - id++; + res.render("dashboard", { + name: req.session.user, + role: req.session.role, + avatar: req.session.avatar, }); - - - res.redirect("/"); - -} - - - -exports.DisableSite = async function (req, res) { - - console.log(req.body) - console.log('Disable Site') - - res.redirect("/"); - -} - - -exports.EnableSite = async function (req, res) { - - console.log(req.body) - console.log('Enable Site') - - res.redirect("/"); } \ No newline at end of file diff --git a/controllers/images.js b/controllers/images.js index 78c282e..8b6c242 100644 --- a/controllers/images.js +++ b/controllers/images.js @@ -1,14 +1,18 @@ +import { docker } from '../app.js'; +export const Images = async function(req, res) { -exports.Images = async function(req, res) { + const allImages = await docker.listImages({ all: true }); + + for (let i = 0; i < allImages.length; i++) { + console.log(`Image ${i}:`) + console.log(`repoTags: ${allImages[i].repoTags}`) + } - - // Render the home page - res.render("pages/images", { + res.render("images", { name: req.session.user, role: req.session.role, avatar: req.session.avatar, - isLoggedIn: true, }); } \ No newline at end of file diff --git a/controllers/login.js b/controllers/login.js new file mode 100644 index 0000000..c6eb8f8 --- /dev/null +++ b/controllers/login.js @@ -0,0 +1,72 @@ +import { User } from '../database/models.js'; +import { Syslog } from '../database/models.js'; +import bcrypt from 'bcrypt'; + +export const Login = function(req,res){ + if(req.session.user){ + res.redirect("/logout"); + }else{ + res.render("login",{ + "error":"", + }); + } +} + +export const submitLogin = async function(req,res){ + + let { email, password } = req.body; + + if(email && password){ + + let existingUser = await User.findOne({ where: {email:email}}); + if(existingUser){ + + let match = await bcrypt.compare(password,existingUser.password); + + if(match){ + + let currentDate = new Date(); + let newLogin = currentDate.toLocaleString(); + await User.update({lastLogin: newLogin}, {where: {UUID:existingUser.UUID}}); + + req.session.user = existingUser.username; + req.session.UUID = existingUser.UUID; + req.session.role = existingUser.role; + req.session.avatar = existingUser.avatar; + + const syslog = await Syslog.create({ + user: req.session.user, + email: email, + event: "Successful Login", + message: "User logged in successfully", + ip: req.socket.remoteAddress + }); + + + res.redirect("/"); + }else{ + + const syslog = await Syslog.create({ + user: null, + email: email, + event: "Bad Login", + message: "Invalid password", + ip: req.socket.remoteAddress + }); + + res.render("login",{ + "error":"Invalid password", + }); + } + }else{ + res.render("login",{ + "error":"User with that email does not exist.", + }); + } + }else{ + res.status(400); + res.render("login",{ + "error":"Please fill in all the fields.", + }); + } +} \ No newline at end of file diff --git a/controllers/networks.js b/controllers/networks.js index af6f43c..3d64242 100644 --- a/controllers/networks.js +++ b/controllers/networks.js @@ -1,14 +1,13 @@ -const User = require('../database/UserModel'); +import { docker } from '../app.js'; -exports.Networks = async function(req, res) { - - // Render the home page - res.render("pages/users", { +export const Networks = async function(req, res) { + + + res.render("networks", { name: req.session.user, role: req.session.role, avatar: req.session.avatar, - isLoggedIn: true, }); } \ No newline at end of file diff --git a/controllers/register.js b/controllers/register.js new file mode 100644 index 0000000..c350eb6 --- /dev/null +++ b/controllers/register.js @@ -0,0 +1,96 @@ +import { User, Syslog } from '../database/models.js'; +import bcrypt from 'bcrypt'; + +let SECRET = process.env.SECRET || "MrWiskers" + +export const Register = function(req,res){ + if(req.session.user){ + res.redirect("/logout"); + } else { + res.render("register",{ + "error":"", + }); + } +} + + + +export const submitRegister = async function(req,res){ + + let { name, username, email, password, confirmPassword, avatar, tos, secret } = req.body; + + + if (secret != SECRET) { + const syslog = await Syslog.create({ + user: username, + email: email, + event: "Failed Registration", + message: "Invalid secret", + ip: req.socket.remoteAddress + }); + } + + if((name && email && password && confirmPassword && username && tos) && (secret == SECRET) && (password == confirmPassword)){ + + async function userRole () { + let userCount = await User.count(); + if(userCount == 0){ + return "admin"; + }else{ + return "user"; + } + } + + let existingUser = await User.findOne({ where: {email:email}}); + if(!existingUser){ + + try { + const user = await User.create({ + name: name, + username: username, + email: email, + password: bcrypt.hashSync(password,10), + role: await userRole(), + group: 'all', + avatar: `` + }); + + // make sure the user was created and get the UUID. + let newUser = await User.findOne({ where: {email:email}}); + let match = await bcrypt.compare(password,newUser.password); + + if(match){ + req.session.user = newUser.username; + req.session.UUID = newUser.UUID; + req.session.role = newUser.role; + req.session.avatar = newUser.avatar; + + const syslog = await Syslog.create({ + user: req.session.user, + email: email, + event: "Successful Registration", + message: "User registered successfully", + ip: req.socket.remoteAddress + }); + + res.redirect("/"); + } + } catch(err) { + res.render("register",{ + "error":"Something went wrong when creating account.", + }); + } + + } else { + // return an error. + res.render("register",{ + "error":"User with that email already exists.", + }); + } + } else { + // Redirect to the signup page. + res.render("register",{ + "error":"Please fill in all the fields and accept TOS.", + }); + } +} \ No newline at end of file diff --git a/controllers/settings.js b/controllers/settings.js index 16a199a..a3196f6 100644 --- a/controllers/settings.js +++ b/controllers/settings.js @@ -1,14 +1,9 @@ -const User = require('../database/UserModel.js'); -const Server = require('../database/ServerModel.js'); -exports.Settings = async function(req, res) { +export const Settings = (req, res) => { - // Render the home page - res.render("pages/settings", { + res.render("settings", { name: req.session.user, role: req.session.role, avatar: req.session.avatar, - isLoggedIn: true }); - } \ No newline at end of file diff --git a/controllers/syslogs.js b/controllers/syslogs.js new file mode 100644 index 0000000..aa2d94a --- /dev/null +++ b/controllers/syslogs.js @@ -0,0 +1,36 @@ +import { Syslog } from '../database/models.js'; + +export const Syslogs = async function(req, res) { + + let logs = ''; + + const syslogs = await Syslog.findAll({ + order: [ + ['id', 'DESC'] + ] + }); + + for (const log of syslogs) { + let date = (log.createdAt).toDateString(); + let time = (log.createdAt).toLocaleTimeString(); + let datetime = `${time} ${date}`; + + logs += ` + ${log.id} + ${log.user} + ${log.email} + ${log.event} + ${log.message} + ${log.ip} + ${datetime} + ` + } + + res.render("syslogs", { + name: req.session.user || 'Dev', + role: req.session.role || 'Dev', + avatar: req.session.avatar || '', + logs: logs + }); + +} \ No newline at end of file diff --git a/controllers/users.js b/controllers/users.js index 9ddae52..1968c47 100644 --- a/controllers/users.js +++ b/controllers/users.js @@ -1,6 +1,6 @@ -const User = require('../database/UserModel'); +import { User } from '../database/models.js'; -exports.Users = async function(req, res) { +export const Users = async (req, res) => { let user_list = ` @@ -12,36 +12,48 @@ exports.Users = async function(req, res) { Email UUID Role + Last Login Status Actions ` - let users = await User.findAll(); - users.forEach((account) => { - full_name = account.first_name + ' ' + account.last_name; - user_info = ` + let allUsers = await User.findAll(); + allUsers.forEach((account) => { + + let active = 'Active' + let lastLogin = new Date(account.lastLogin); + let currentDate = new Date(); + let days = Math.floor((currentDate - lastLogin) / (1000 * 60 * 60 * 24)); + + if (days > 30) { + active = 'Inactive'; + } + + + + let info = ` - ${user.id} + ${account.id} ${account.avatar} - ${full_name} + ${account.name} ${account.username} ${account.email} ${account.UUID} ${account.role} - Active + ${account.lastLogin} + ${active} Edit ` - user_list += user_info; + user_list += info; }); - // Render the home page - res.render("pages/users", { + + res.render("users", { name: req.session.user, role: req.session.role, avatar: req.session.avatar, - isLoggedIn: true, user_list: user_list }); diff --git a/controllers/volumes.js b/controllers/volumes.js index 66eec83..c3d9548 100644 --- a/controllers/volumes.js +++ b/controllers/volumes.js @@ -1,15 +1,9 @@ -const User = require('../database/UserModel'); +import { docker } from '../app.js'; -exports.Volumes = async function(req, res) { - - - - // Render the home page - res.render("pages/volumes", { +export const Volumes = (req, res) => { + res.render("volumes", { name: req.session.user, role: req.session.role, avatar: req.session.avatar, - isLoggedIn: true, }); - } \ No newline at end of file diff --git a/database/ContainerModel.js b/database/ContainerModel.js deleted file mode 100644 index 0613f4e..0000000 --- a/database/ContainerModel.js +++ /dev/null @@ -1,72 +0,0 @@ -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 - }, - start: { - type: DataTypes.JSON - // allowNull defaults to true - }, - stop: { - type: DataTypes.JSON - // allowNull defaults to true - }, - pause: { - type: DataTypes.JSON - // allowNull defaults to true - }, - restart: { - type: DataTypes.JSON - // allowNull defaults to true - }, - remove: { - type: DataTypes.JSON - // allowNull defaults to true - }, - logs: { - type: DataTypes.JSON - // allowNull defaults to true - }, - update: { - type: DataTypes.JSON - // allowNull defaults to true - }, - -}); - -async function syncModel() { - await sequelize.sync(); - console.log('Containers model synced'); -} - -syncModel(); - - -module.exports = Containers; \ No newline at end of file diff --git a/database/ServerModel.js b/database/ServerModel.js deleted file mode 100644 index f02072a..0000000 --- a/database/ServerModel.js +++ /dev/null @@ -1,46 +0,0 @@ -const { Sequelize, DataTypes } = require('sequelize'); - -const sequelize = new Sequelize({ - dialect: 'sqlite', - storage: './database/db.sqlite', - logging: false -}); - - -const Server = sequelize.define('Server', { - // Model attributes are defined here - timezone: { - type: DataTypes.STRING, - allowNull: false - }, - hwa: { - type: DataTypes.STRING - // allowNull defaults to true - }, - media: { - type: DataTypes.STRING - // allowNull defaults to true - }, - pgid: { - type: DataTypes.STRING - // allowNull defaults to true - }, - puid: { - type: DataTypes.STRING - // allowNull defaults to true - }, - caddy: { - type: DataTypes.STRING - // allowNull defaults to true - }, -}); - -async function syncModel() { - await sequelize.sync(); - console.log('Server model synced'); -} - -syncModel(); - - -module.exports = Server; \ No newline at end of file diff --git a/database/UserModel.js b/database/UserModel.js deleted file mode 100644 index 01ac4f4..0000000 --- a/database/UserModel.js +++ /dev/null @@ -1,63 +0,0 @@ -const { Sequelize, DataTypes } = require('sequelize'); - -const sequelize = new Sequelize({ - dialect: 'sqlite', - storage: './database/db.sqlite', - logging: false -}); - - -const User = sequelize.define('User', { - // Model attributes are defined here - id: { - type: DataTypes.INTEGER, - autoIncrement: true, - primaryKey: true - }, - first_name: { - type: DataTypes.STRING, - allowNull: false - }, - last_name: { - type: DataTypes.STRING - // allowNull defaults to true - }, - username: { - type: DataTypes.STRING - // allowNull defaults to true - }, - email: { - type: DataTypes.STRING - // allowNull defaults to true - }, - password: { - type: DataTypes.STRING, - // allowNull: false - }, - role: { - type: DataTypes.STRING - // allowNull defaults to true - }, - group: { - type: DataTypes.STRING - // allowNull defaults to true - }, - avatar: { - type: DataTypes.STRING - // allowNull defaults to true - }, - UUID: { - type: DataTypes.UUID, - defaultValue: DataTypes.UUIDV4 - } -}); - -async function syncModel() { - await sequelize.sync(); - console.log('User model synced'); -} - -syncModel(); - - -module.exports = User; \ No newline at end of file diff --git a/database/models.js b/database/models.js new file mode 100644 index 0000000..43b4a68 --- /dev/null +++ b/database/models.js @@ -0,0 +1,162 @@ +import { Sequelize, DataTypes } from 'sequelize'; + +// let SQLITE_PASS = process.env.SQLITE_PASS || 'some_long_elaborate_password'; + +// export const sequelize = new Sequelize('dweebui', 'dweebui', SQLITE_PASS, { +// dialect: 'sqlite', +// dialectModulePath: '@journeyapps/sqlcipher', +// storage: './database/database.sqlite', +// logging: false, +// }); + +export const sequelize = new Sequelize({ + dialect: 'sqlite', + storage: './database/database.sqlite', + logging: false, +}); + +export const User = sequelize.define('User', { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true + }, + name: { + type: DataTypes.STRING + }, + username: { + type: DataTypes.STRING, + allowNull: false + }, + email: { + type: DataTypes.STRING, + allowNull: false + }, + password: { + type: DataTypes.STRING, + allowNull: false + }, + role: { + type: DataTypes.STRING + }, + group: { + type: DataTypes.STRING + }, + avatar: { + type: DataTypes.STRING + }, + lastLogin: { + type: DataTypes.STRING + }, + UUID: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + } +}); + +export const Container = sequelize.define('Container', { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true + }, + name: { + type: DataTypes.STRING, + allowNull: false + }, + visibility: { + type: DataTypes.STRING + }, + size: { + type: DataTypes.STRING + }, + group: { + type: DataTypes.STRING + } +}); + +export const Permission = sequelize.define('Permission', { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true + }, + containerName: { + type: DataTypes.STRING, + allowNull: false + }, + containerID: { + type: DataTypes.STRING, + allowNull: false + }, + user: { + type: DataTypes.STRING, + allowNull: false + }, + userID: { + type: DataTypes.STRING, + allowNull: false + }, + install: { + type: DataTypes.STRING, + }, + uninstall: { + type: DataTypes.STRING + }, + edit: { + type: DataTypes.STRING + }, + upgrade: { + type: DataTypes.STRING + }, + start: { + type: DataTypes.STRING + }, + stop: { + type: DataTypes.STRING + }, + restart: { + type: DataTypes.STRING + }, + pause: { + type: DataTypes.STRING + }, + logs: { + type: DataTypes.STRING + }, + hide: { + type: DataTypes.STRING + }, + view: { + type: DataTypes.STRING + }, + reset_view: { + type: DataTypes.STRING + }, +}); + + +export const Syslog = sequelize.define('Syslog', { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true + }, + user: { + type: DataTypes.STRING + }, + email: { + type: DataTypes.STRING + }, + event: { + type: DataTypes.STRING, + allowNull: false + }, + message: { + type: DataTypes.STRING, + allowNull: false + }, + ip : { + type: DataTypes.STRING + }, +}); \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml index 8e05778..b046ab9 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -2,9 +2,9 @@ version: "3.9" services: dweebui: container_name: dweebui - image: lllllllillllllillll/dweebui:v0.09-dev - # build: - # context: . + # image: lllllllillllllillll/dweebui:v0.20 + build: + context: . environment: NODE_ENV: production PORT: 8000 @@ -14,17 +14,13 @@ services: - 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 + - dweebui_net volumes: dweebui: - caddyfiles: networks: - dweeb_network: + dweebui_net: driver: bridge diff --git a/functions/compose.js b/functions/compose.js deleted file mode 100644 index 153f982..0000000 --- a/functions/compose.js +++ /dev/null @@ -1,205 +0,0 @@ -const { writeFileSync, mkdirSync, readFileSync } = require("fs"); -const yaml = require('js-yaml'); - -const { exec, execSync } = require("child_process"); - -const { docker } = require('./system'); - -var DockerodeCompose = require('dockerode-compose'); - - -module.exports.install = async function (data) { - - console.log(`[Start of install function]`); - - let { service_name, name, image, command_check, command, net_mode, restart_policy } = data; - let { port0, port1, port2, port3, port4, port5 } = data; - let { volume0, volume1, volume2, volume3, volume4, volume5 } = data; - let { env0, env1, env2, env3, env4, env5, env6, env7, env8, env9, env10, env11 } = data; - let { label0, label1, label2, label3, label4, label5, label6, label7, label8, label9, label10, label11 } = data; - - - if ((service_name.includes('caddy')) || (name.includes('caddy'))) { - req.app.locals.caddy = 'enabled'; - } - - let docker_volumes = []; - - if (image.startsWith('https://')){ - mkdirSync(`./appdata/${name}`, { recursive: true }); - execSync(`curl -o ./appdata/${name}/${name}_stack.yml -L ${image}`); - console.log(`Downloaded stackfile: ${image}`); - let stackfile = yaml.load(readFileSync(`./appdata/${name}/${name}_stack.yml`, 'utf8')); - let services = Object.keys(stackfile.services); - - for ( let i = 0; i < services.length; i++ ) { - try { - console.log(stackfile.services[Object.keys(stackfile.services)[i]].environment); - } catch { console.log('no env') } - } - - } else { - - let compose_file = `version: '3'`; - compose_file += `\nservices:` - compose_file += `\n ${service_name}:` - compose_file += `\n container_name: ${name}`; - compose_file += `\n image: ${image}`; - - // Command - if (command_check == 'on') { - compose_file += `\n command: ${command}` - } - - // Network mode - if (net_mode == 'host') { - compose_file += `\n network_mode: 'host'` - } - else if (net_mode != 'host' && net_mode != 'docker') { - compose_file += `\n network_mode: '${net_mode}'` - } - - // Restart policy - if (restart_policy != '') { - compose_file += `\n restart: ${restart_policy}` - } - - // Ports - if ((port0 == 'on' || port1 == 'on' || port2 == 'on' || port3 == 'on' || port4 == 'on' || port5 == 'on') && (net_mode != 'host')) { - compose_file += `\n ports:` - - for (let i = 0; i < 6; i++) { - if (data[`port${i}`] == 'on') { - compose_file += `\n - ${data[`port_${i}_external`]}:${data[`port_${i}_internal`]}/${data[`port_${i}_protocol`]}` - } - } - } - - // Volumes - if (volume0 == 'on' || volume1 == 'on' || volume2 == 'on' || volume3 == 'on' || volume4 == 'on' || volume5 == 'on') { - compose_file += `\n volumes:` - - for (let i = 0; i < 6; i++) { - - // if volume is on and neither bind or container is empty, it's a bind mount (ex /mnt/user/appdata/config:/config ) - if ((data[`volume${i}`] == 'on') && (data[`volume_${i}_bind`] != '') && (data[`volume_${i}_container`] != '')) { - compose_file += `\n - ${data[`volume_${i}_bind`]}:${data[`volume_${i}_container`]}:${data[`volume_${i}_readwrite`]}` - } - - // if bind is empty create a docker volume (ex container_name_config:/config) convert any '/' in container name to '_' - else if ((data[`volume${i}`] == 'on') && (data[`volume_${i}_bind`] == '') && (data[`volume_${i}_container`] != '')) { - let volume_name = data[`volume_${i}_container`].replace(/\//g, '_'); - compose_file += `\n - ${name}_${volume_name}:${data[`volume_${i}_container`]}:${data[`volume_${i}_readwrite`]}` - docker_volumes.push(`${name}_${volume_name}`); - } - } - } - - // Environment variables - if (env0 == 'on' || env1 == 'on' || env2 == 'on' || env3 == 'on' || env4 == 'on' || env5 == 'on' || env6 == 'on' || env7 == 'on' || env8 == 'on' || env9 == 'on' || env10 == 'on' || env11 == 'on') { - compose_file += `\n environment:` - } - for (let i = 0; i < 12; i++) { - if (data[`env${i}`] == 'on') { - compose_file += `\n - ${data[`env_${i}_name`]}=${data[`env_${i}_default`]}` - - } - } - - // Add labels - if (label0 == 'on' || label1 == 'on' || label2 == 'on' || label3 == 'on' || label4 == 'on' || label5 == 'on' || label6 == 'on' || label7 == 'on' || label8 == 'on' || label9 == 'on' || label10 == 'on' || label11 == 'on') { - compose_file += `\n labels:` - } - for (let i = 0; i < 12; i++) { - if (data[`label${i}`] == 'on') { - compose_file += `\n - ${data[`label_${i}_name`]}=${data[`label_${i}_value`]}` - } - } - - // Add privileged mode - - if (data.privileged == 'on') { - compose_file += `\n privileged: true` - } - - - // Add hardware acceleration to the docker-compose file if one of the environment variables has the label DRINODE - if (env0 == 'on' || env1 == 'on' || env2 == 'on' || env3 == 'on' || env4 == 'on' || env5 == 'on' || env6 == 'on' || env7 == 'on' || env8 == 'on' || env9 == 'on' || env10 == 'on' || env11 == 'on') { - for (let i = 0; i < 12; i++) { - if (data[`env${i}`] == 'on') { - if (data[`env_${i}_name`] == 'DRINODE') { - compose_file += `\n deploy:` - compose_file += `\n resources:` - compose_file += `\n reservations:` - compose_file += `\n devices:` - compose_file += `\n - driver: nvidia` - compose_file += `\n count: 1` - compose_file += `\n capabilities: [gpu]` - } - } - } - } - - - // add any docker volumes to the docker-compose file - if ( docker_volumes.length > 0 ) { - compose_file += `\n` - compose_file += `\nvolumes:` - - // check docker_volumes for duplicates and remove them completely - docker_volumes = docker_volumes.filter((item, index) => docker_volumes.indexOf(item) === index) - - for (let i = 0; i < docker_volumes.length; i++) { - if ( docker_volumes[i] != '') { - compose_file += `\n ${docker_volumes[i]}:` - } - } - } - - try { - mkdirSync(`./appdata/${name}`, { recursive: true }); - writeFileSync(`./appdata/${name}/docker-compose.yml`, compose_file, function (err) { console.log(err) }); - - } catch { console.log('error creating directory or compose file') } - - try { - var compose = new DockerodeCompose(docker, `./appdata/${name}/docker-compose.yml`, `${name}`); - - (async () => { - await compose.pull(); - await compose.up(); - })(); - - } catch { console.log('error running compose file')} - - } - - -} - - - -module.exports.uninstall = async function (data) { - - - if (data.confirm == 'Yes') { - - - var containerName = docker.getContainer(`${data.service_name}`); - - try { - containerName.stop(function (err, data) { - if (data) { - containerName.remove(function (err, data) { - }); - } - }); - } catch { - containerName.remove(function (err, data) { - }); - } - - } - - -} \ No newline at end of file diff --git a/functions/package_manager.js b/functions/package_manager.js deleted file mode 100644 index 9f5065a..0000000 --- a/functions/package_manager.js +++ /dev/null @@ -1,194 +0,0 @@ -const { writeFileSync, mkdirSync, readFileSync } = require("fs"); -const yaml = require('js-yaml'); - -const { execSync } = require("child_process"); - -const { docker } = require('./system'); - -var DockerodeCompose = require('dockerode-compose'); - - -module.exports.install = async function (data) { - - console.log(`[Start of install function]`); - - let { service_name, name, image, command_check, command, net_mode, restart_policy } = data; - let { port0, port1, port2, port3, port4, port5 } = data; - let { volume0, volume1, volume2, volume3, volume4, volume5 } = data; - let { env0, env1, env2, env3, env4, env5, env6, env7, env8, env9, env10, env11 } = data; - let { label0, label1, label2, label3, label4, label5, label6, label7, label8, label9, label10, label11 } = data; - - let docker_volumes = []; - - if (image.startsWith('https://')){ - mkdirSync(`./appdata/${name}`, { recursive: true }); - execSync(`curl -o ./appdata/${name}/${name}_stack.yml -L ${image}`); - console.log(`Downloaded stackfile: ${image}`); - let stackfile = yaml.load(readFileSync(`./appdata/${name}/${name}_stack.yml`, 'utf8')); - let services = Object.keys(stackfile.services); - - for ( let i = 0; i < services.length; i++ ) { - try { - console.log(stackfile.services[Object.keys(stackfile.services)[i]].environment); - } catch { console.log('no env') } - } - - } else { - - let compose_file = `version: '3'`; - compose_file += `\nservices:` - compose_file += `\n ${service_name}:` - compose_file += `\n container_name: ${name}`; - compose_file += `\n image: ${image}`; - - // Command - if (command_check == 'on') { - compose_file += `\n command: ${command}` - } - - // Network mode - if (net_mode == 'host') { - compose_file += `\n network_mode: 'host'` - } - else if (net_mode != 'host' && net_mode != 'docker') { - compose_file += `\n network_mode: '${net_mode}'` - } - - // Restart policy - if (restart_policy != '') { - compose_file += `\n restart: ${restart_policy}` - } - - // Ports - if ((port0 == 'on' || port1 == 'on' || port2 == 'on' || port3 == 'on' || port4 == 'on' || port5 == 'on') && (net_mode != 'host')) { - compose_file += `\n ports:` - - for (let i = 0; i < 6; i++) { - if (data[`port${i}`] == 'on') { - compose_file += `\n - ${data[`port_${i}_external`]}:${data[`port_${i}_internal`]}/${data[`port_${i}_protocol`]}` - } - } - } - - // Volumes - if (volume0 == 'on' || volume1 == 'on' || volume2 == 'on' || volume3 == 'on' || volume4 == 'on' || volume5 == 'on') { - compose_file += `\n volumes:` - - for (let i = 0; i < 6; i++) { - - // if volume is on and neither bind or container is empty, it's a bind mount (ex /mnt/user/appdata/config:/config ) - if ((data[`volume${i}`] == 'on') && (data[`volume_${i}_bind`] != '') && (data[`volume_${i}_container`] != '')) { - compose_file += `\n - ${data[`volume_${i}_bind`]}:${data[`volume_${i}_container`]}:${data[`volume_${i}_readwrite`]}` - } - - // if bind is empty create a docker volume (ex container_name_config:/config) convert any '/' in container name to '_' - else if ((data[`volume${i}`] == 'on') && (data[`volume_${i}_bind`] == '') && (data[`volume_${i}_container`] != '')) { - let volume_name = data[`volume_${i}_container`].replace(/\//g, '_'); - compose_file += `\n - ${name}_${volume_name}:${data[`volume_${i}_container`]}:${data[`volume_${i}_readwrite`]}` - docker_volumes.push(`${name}_${volume_name}`); - } - } - } - - // Environment variables - if (env0 == 'on' || env1 == 'on' || env2 == 'on' || env3 == 'on' || env4 == 'on' || env5 == 'on' || env6 == 'on' || env7 == 'on' || env8 == 'on' || env9 == 'on' || env10 == 'on' || env11 == 'on') { - compose_file += `\n environment:` - } - for (let i = 0; i < 12; i++) { - if (data[`env${i}`] == 'on') { - compose_file += `\n - ${data[`env_${i}_name`]}=${data[`env_${i}_default`]}` - - } - } - - // Add labels - if (label0 == 'on' || label1 == 'on' || label2 == 'on' || label3 == 'on' || label4 == 'on' || label5 == 'on' || label6 == 'on' || label7 == 'on' || label8 == 'on' || label9 == 'on' || label10 == 'on' || label11 == 'on') { - compose_file += `\n labels:` - } - for (let i = 0; i < 12; i++) { - if (data[`label${i}`] == 'on') { - compose_file += `\n - ${data[`label_${i}_name`]}=${data[`label_${i}_value`]}` - } - } - - // Add privileged mode - - if (data.privileged == 'on') { - compose_file += `\n privileged: true` - } - - - // Add hardware acceleration to the docker-compose file if one of the environment variables has the label DRINODE - if (env0 == 'on' || env1 == 'on' || env2 == 'on' || env3 == 'on' || env4 == 'on' || env5 == 'on' || env6 == 'on' || env7 == 'on' || env8 == 'on' || env9 == 'on' || env10 == 'on' || env11 == 'on') { - for (let i = 0; i < 12; i++) { - if (data[`env${i}`] == 'on') { - if (data[`env_${i}_name`] == 'DRINODE') { - compose_file += `\n deploy:` - compose_file += `\n resources:` - compose_file += `\n reservations:` - compose_file += `\n devices:` - compose_file += `\n - driver: nvidia` - compose_file += `\n count: 1` - compose_file += `\n capabilities: [gpu]` - } - } - } - } - - - // add any docker volumes to the docker-compose file - if ( docker_volumes.length > 0 ) { - compose_file += `\n` - compose_file += `\nvolumes:` - - // check docker_volumes for duplicates and remove them completely - docker_volumes = docker_volumes.filter((item, index) => docker_volumes.indexOf(item) === index) - - for (let i = 0; i < docker_volumes.length; i++) { - if ( docker_volumes[i] != '') { - compose_file += `\n ${docker_volumes[i]}:` - } - } - } - - try { - mkdirSync(`./appdata/${name}`, { recursive: true }); - writeFileSync(`./appdata/${name}/docker-compose.yml`, compose_file, function (err) { console.log(err) }); - - } catch { console.log('error creating directory or compose file') } - - try { - var compose = new DockerodeCompose(docker, `./appdata/${name}/docker-compose.yml`, `${name}`); - - (async () => { - await compose.pull(); - await compose.up(); - })(); - - } catch { console.log('error running compose file')} - - } - - -} - - - -module.exports.uninstall = async function (data) { - if (data.confirm == 'Yes') { - console.log(`Uninstalling ${data.service_name}: ${data}`); - var containerName = docker.getContainer(`${data.service_name}`); - try { - await containerName.stop(); - console.log(`Stopped ${data.service_name} container`); - } catch { - console.log(`Error stopping ${data.service_name} container`); - } - try { - await containerName.remove(); - console.log(`Removed ${data.service_name} container`); - } catch { - console.log(`Error removing ${data.service_name} container`); - } - } -} \ No newline at end of file diff --git a/functions/system.js b/functions/system.js deleted file mode 100644 index a3a1df6..0000000 --- a/functions/system.js +++ /dev/null @@ -1,382 +0,0 @@ -const { currentLoad, mem, networkStats, fsSize, dockerContainerStats, dockerImages, networkInterfaces, dockerInfo } = 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/ContainerModel'); - -// 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(); - const netUsage = await networkStats(); - const diskUsage = await fsSize(); - - const info = { - cpu: Math.round(cpuUsage.currentLoad), - ram: Math.round((ramUsage.active / ramUsage.total) * 100), - tx: netUsage[0].tx_bytes, - rx: netUsage[0].rx_bytes, - disk: diskUsage[0].use, - }; - - return info; -} - - - -module.exports.containerList = async function () { - let card_list = ''; - - const data = await docker.listContainers({ all: true }); - for (const container of data) { - - - if (!hidden.includes(container.Names[0].slice(1))) { - - let imageVersion = container.Image.split('/'); - let service = imageVersion[imageVersion.length - 1].split(':')[0]; - - let containerId = docker.getContainer(container.Id); - let containerInfo = await containerId.inspect(); - - // Get ports ////////////////////////// - let ports_list = []; - try { - for (const [key, value] of Object.entries(containerInfo.HostConfig.PortBindings)) { - let ports = { - check : 'checked', - external: value[0].HostPort, - internal: key.split('/')[0], - protocol: key.split('/')[1] - } - ports_list.push(ports); - } - } catch { - // console.log('no ports') - } - - for (let i = 0; i < 12; i++) { - if (ports_list[i] == undefined) { - let ports = { - check : '', - external: '', - internal: '', - protocol: '' - } - ports_list[i] = ports; - } - } ///////////////////////////////////// - - - // Get volumes //////////////////////// - let volumes_list = []; - try { for (const [key, value] of Object.entries(containerInfo.HostConfig.Binds)) { - let volumes = { - check : 'checked', - bind: value.split(':')[0], - container: value.split(':')[1], - readwrite: value.split(':')[2] - } - volumes_list.push(volumes); - }} catch { - // console.log('no volumes') - } - for (let i = 0; i < 12; i++) { - if (volumes_list[i] == undefined) { - let volumes = { - check : '', - bind: '', - container: '', - readwrite: '' - } - volumes_list[i] = volumes; - } - } ///////////////////////////////////// - - - // Get environment variables. - let environment_variables = []; - try { for (const [key, value] of Object.entries(containerInfo.Config.Env)) { - let env = { - check : 'checked', - name: value.split('=')[0], - default: value.split('=')[1] - } - environment_variables.push(env); - }} catch { console.log('no env') } - for (let i = 0; i < 12; i++) { - if (environment_variables[i] == undefined) { - let env = { - check : '', - name: '', - default: '' - } - environment_variables[i] = env; - } - } - - // Get labels. - let labels = []; - for (const [key, value] of Object.entries(containerInfo.Config.Labels)) { - let label = { - check : 'checked', - name: key, - value: value - } - labels.push(label); - } - for (let i = 0; i < 12; i++) { - if (labels[i] == undefined) { - let label = { - check : '', - name: '', - value: '' - } - labels[i] = label; - } - } - - - let container_info = { - name: container.Names[0].slice(1), - service: service, - id: container.Id, - state: container.State, - image: container.Image, - external_port: ports_list[0].external || 0, - internal_port: ports_list[0].internal || 0, - ports: ports_list, - volumes: volumes_list, - environment_variables: environment_variables, - labels: labels, - IPv4: IPv4, - style: "Compact" - } - - let dockerCard = dashCard(container_info); - - card_list += dockerCard; - } - - } - - return card_list; -} - - - - - - - -module.exports.containerStats = async function () { - - let container_stats = []; - const data = await docker.listContainers({ all: true }); - - for (const container of data) { - - if (!hidden.includes(container.Names[0].slice(1))) { - const stats = await dockerContainerStats(container.Id); - - let container_stat = { - name: container.Names[0].slice(1), - cpu: Math.round(stats[0].cpuPercent), - ram: Math.round(stats[0].memPercent) - } - - //push stats to an array - container_stats.push(container_stat); - } - } - return container_stats; -} - - - - - - -module.exports.containerAction = async function (data) { - - let { user, role, action, container, state } = data; - - console.log(`${user} wants to: ${action} ${container}`); - - if (role == 'admin') { - var containerName = docker.getContainer(container); - - if ((action == 'start') && (state == 'stopped')) { - containerName.start(); - } else if ((action == 'start') && (state == 'paused')) { - containerName.unpause(); - } else if ((action == 'stop') && (state != 'stopped')) { - containerName.stop(); - } else if ((action == 'pause') && (state == 'running')) { - containerName.pause(); - } else if ((action == 'pause') && (state == 'paused')) { - containerName.unpause(); - } else if (action == 'restart') { - containerName.restart(); - } - } else { - console.log('User is not an admin'); - } -} - - - -module.exports.containerExec = async function (data) { - - let { container, command } = data; - - var containerName = docker.getContainer(container); - - var options = { - Cmd: ['/bin/sh', '-c', command], - AttachStdout: true, - AttachStderr: true, - Tty: true - }; - - containerName.exec(options, function (err, exec) { - if (err) return; - - exec.start(function (err, stream) { - if (err) return; - - containerName.modem.demuxStream(stream, process.stdout, process.stderr); - - exec.inspect(function (err, data) { - if (err) return; - - - }); - }); - }); - -} - - - - - - - - - - -module.exports.containerLogs = function (data) { - return new Promise((resolve, reject) => { - let logString = ''; - - var options = { - follow: false, - stdout: true, - stderr: false, - timestamps: false - }; - - var containerName = docker.getContainer(data); - - containerName.logs(options, function (err, stream) { - if (err) { - reject(err); - return; - } - - const readableStream = Readable.from(stream); - - readableStream.on('data', function (chunk) { - logString += chunk.toString('utf8'); - }); - - readableStream.on('end', function () { - resolve(logString); - }); - }); - }); -}; - - - -module.exports.dockerImages = async function () { - - // get the names, tags, status, created date, and size of all images - - const data1 = await dockerImages({ all: true }); - - const data2 = await docker.listImages({ all: true }); - - // for ( i = 0; i < data.length; i++) { - // console.log(`Image ${i}:`) - // console.log(`repoTags: ${data[i].repoTags}`) - // } - - // console.log(data1); - - console.log(data2); - -} - - -module.exports.dockerVolumes = async function () { - let volume_list = ''; - - const data = await docker.listVolumes(); - - return data; - - // for (const volume of data.Volumes) { - - // let volume_info = { - // name: volume.Name, - // style: "Compact" - // } - - // let dockerCard = dashCard(volume_info); - - // volume_list += dockerCard; - // } - - // return volume_list; -} - - -module.exports.dockerNetworks = async function () { - let network_list = ''; - - const data = await docker.listNetworks(); - - return data; - - // for (const network of data) { - - // let network_info = { - // name: network.Name, - // style: "Compact" - // } - - // let dockerCard = dashCard(network_info); - - // network_list += dockerCard; - // } - - // return network_list; -} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index f99496f..228d68c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,20 +9,540 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "bcrypt": "^5.1.0", - "child_process": "^1.0.2", + "@babel/register": "^7.23.7", + "@journeyapps/sqlcipher": "^5.3.1", + "@socket.io/admin-ui": "^0.5.1", + "bcrypt": "^5.1.1", + "chai": "^5.0.0", "compression": "^1.7.4", - "dockerode": "^4.0.0", - "dockerode-compose": "^1.4.0", + "cors": "^2.8.5", + "dockerode": "^4.0.1", "ejs": "^3.1.9", "express": "^4.18.2", "express-session": "^1.17.3", "helmet": "^7.1.0", - "js-yaml": "^4.1.0", + "mocha": "^10.2.0", "sequelize": "^6.35.2", - "socket.io": "^4.6.1", + "sinon": "^17.0.1", + "socket.io": "^4.7.2", "sqlite3": "^5.1.6", - "systeminformation": "^5.21.20" + "stream": "^0.0.2", + "supertest": "^6.3.3", + "systeminformation": "^5.21.22" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "peer": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "peer": true, + "dependencies": { + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "peer": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "peer": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "peer": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "peer": true + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "peer": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "peer": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", + "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.7.tgz", + "integrity": "sha512-+UpDgowcmqe36d4NwqvKsyPMlOLNGMsfMmQ5WGCu+siCe3t3dfe9njrzGfdN4qq+bcNUt0+Vw6haRxBOycs4dw==", + "peer": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.7", + "@babel/parser": "^7.23.6", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.7", + "@babel/types": "^7.23.6", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "peer": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", + "peer": true, + "dependencies": { + "@babel/types": "^7.23.6", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", + "peer": true, + "dependencies": { + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "peer": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "peer": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "peer": true + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "peer": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "peer": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "peer": true, + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "peer": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "peer": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "peer": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.7.tgz", + "integrity": "sha512-6AMnjCoC8wjqBzDHkuqpa7jAKwvMo4dC+lr/TFBz+ucfulO1XMpDnwWPGBNwClOKZ8h6xn5N81W/R5OrcKtCbQ==", + "peer": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.7", + "@babel/types": "^7.23.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "peer": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "peer": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "peer": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "peer": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "peer": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "peer": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "peer": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz", + "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==", + "peer": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/register": { + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.23.7.tgz", + "integrity": "sha512-EjJeB6+kvpk+Y5DAkEAmbOBEFkh9OASx0huoEkqYTFxAZHzOAX2Oh5uwAUuL2rUddqfM0SA+KPXV2TbzoZ2kvQ==", + "dependencies": { + "clone-deep": "^4.0.1", + "find-cache-dir": "^2.0.0", + "make-dir": "^2.1.0", + "pirates": "^4.0.6", + "source-map-support": "^0.5.16" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/register/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@babel/register/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/@babel/template": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.7.tgz", + "integrity": "sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg==", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.6", + "@babel/types": "^7.23.6", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz", + "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==", + "peer": true, + "dependencies": { + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@balena/dockerignore": { @@ -36,6 +556,64 @@ "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", "optional": true }, + "node_modules/@journeyapps/sqlcipher": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@journeyapps/sqlcipher/-/sqlcipher-5.3.1.tgz", + "integrity": "sha512-d1mncvE1f9+A/P3HJLwjSAgY6/IDAf2TPLGRgTLQN4hRO7J0QdqEC6CRUvPFJXcsGHn9JkEHQnPpzFXh81ocLA==", + "hasInstallScript": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.0", + "node-addon-api": "^3.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "peer": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "peer": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "peer": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "peer": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "peer": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "node_modules/@mapbox/node-pre-gyp": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", @@ -79,6 +657,58 @@ "node": ">=10" } }, + "node_modules/@sinonjs/commons": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.2.2.tgz", + "integrity": "sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw==", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@sinonjs/samsam": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz", + "integrity": "sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==", + "dependencies": { + "@sinonjs/commons": "^2.0.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" + } + }, + "node_modules/@sinonjs/samsam/node_modules/@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/text-encoding": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", + "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==" + }, + "node_modules/@socket.io/admin-ui": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@socket.io/admin-ui/-/admin-ui-0.5.1.tgz", + "integrity": "sha512-1dlGL2FGm6T+uL1e6iDvbo2eCINwvW7iVbjIblwh5kPPRM1SP8lmZrbFZf4QNJ/cqQ+JLcx49eXGM9WAB4TK7w==", + "dependencies": { + "@types/bcryptjs": "^2.4.2", + "bcryptjs": "^2.4.3", + "debug": "~4.3.1" + }, + "peerDependencies": { + "socket.io": ">=3.1.0" + } + }, "node_modules/@socket.io/component-emitter": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", @@ -93,6 +723,11 @@ "node": ">= 6" } }, + "node_modules/@types/bcryptjs": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.6.tgz", + "integrity": "sha512-9xlo6R2qDs5uixm0bcIqCeMCE6HiQsIyel9KQySStiyqNl2tnj2mP3DX1Nf56MD6KMenNNlBBsy3LJ7gUEQPXQ==" + }, "node_modules/@types/cookie": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", @@ -120,9 +755,9 @@ "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" }, "node_modules/@types/node": { - "version": "20.10.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.4.tgz", - "integrity": "sha512-D08YG6rr8X90YB56tSIuBaddy/UXAA9RKJoFvrsnogAum/0pmjkgi4+2nx96A330FmioegBWmEYQ+syqCFaveg==", + "version": "20.10.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.6.tgz", + "integrity": "sha512-Vac8H+NlRNNlAmDfGUP7b5h/KA+AtWIzuXy0E6OyP8f1tCLYAtPvKRRDJjAPqhpCb0t6U2j7/xqAuLEebW2kiw==", "dependencies": { "undici-types": "~5.26.4" } @@ -185,6 +820,14 @@ "node": ">=8" } }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "engines": { + "node": ">=6" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -207,6 +850,18 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/aproba": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", @@ -234,6 +889,11 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" + }, "node_modules/asn1": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", @@ -242,11 +902,24 @@ "safer-buffer": "~2.1.0" } }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "engines": { + "node": ">=12" + } + }, "node_modules/async": { "version": "3.2.5", "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -300,6 +973,32 @@ "tweetnacl": "^0.14.3" } }, + "node_modules/bcrypt/node_modules/node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" + }, + "node_modules/bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==" + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, "node_modules/bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -333,6 +1032,14 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/body-parser/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/body-parser/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -355,6 +1062,54 @@ "concat-map": "0.0.1" } }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==" + }, + "node_modules/browserslist": { + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", + "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "peer": true, + "dependencies": { + "caniuse-lite": "^1.0.30001565", + "electron-to-chromium": "^1.4.601", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -378,6 +1133,11 @@ "ieee754": "^1.1.13" } }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, "node_modules/buildcheck": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.6.tgz", @@ -388,9 +1148,9 @@ } }, "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", "engines": { "node": ">= 0.8" } @@ -437,6 +1197,52 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001574", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001574.tgz", + "integrity": "sha512-BtYEK4r/iHt/txm81KBudCUcTy7t+s9emrIaHqjYurQ10x71zJ5VQ9x1dYPcz/b+pKSp4y/v1xSI67A+LzpNyg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "peer": true + }, + "node_modules/chai": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.0.0.tgz", + "integrity": "sha512-HO5p0oEKd5M6HEcwOkNAThAE3j960vIZvVcc0t2tI06Dd0ATu69cEnMB2wOhC5/ZyQ6m67w3ePjU/HzXsSsdBA==", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.0.0", + "deep-eql": "^5.0.1", + "loupe": "^3.0.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -452,10 +1258,39 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/child_process": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/child_process/-/child_process-1.0.2.tgz", - "integrity": "sha512-Wmza/JzL0SiWz7kl6MhIKT5ceIlnFPJX+lwUGj7Clhy5MMldsSoJR0+uvRzOS5Kv45Mq7t1PoE8TsOA9bzvb6g==" + "node_modules/check-error": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.0.0.tgz", + "integrity": "sha512-tjLAOBHKVxtPoHe/SA7kNOMvhCRdCJ3vETdeY0RuAc9popf+hyaSV6ZEg9hr4cpWF7jmo/JSWEnLDrnijS9Tog==", + "engines": { + "node": ">= 16" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } }, "node_modules/chownr": { "version": "2.0.0", @@ -474,6 +1309,29 @@ "node": ">=6" } }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -498,6 +1356,30 @@ "color-support": "bin.js" } }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" + }, + "node_modules/component-emitter": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", + "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/compressible": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", @@ -526,14 +1408,6 @@ "node": ">= 0.8.0" } }, - "node_modules/compression/node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/compression/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -547,11 +1421,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/compression/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -573,6 +1442,25 @@ "node": ">= 0.6" } }, + "node_modules/content-disposition/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/content-type": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", @@ -581,6 +1469,12 @@ "node": ">= 0.6" } }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "peer": true + }, "node_modules/cookie": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", @@ -594,6 +1488,11 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "node_modules/cookiejar": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==" + }, "node_modules/cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", @@ -636,6 +1535,47 @@ } } }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-eql": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.1.tgz", + "integrity": "sha512-nwQCf6ne2gez3o1MxWifqkciwt0zhl0LO1/UwVu4uMBuPmflWM4oQ70XMqHqnBJA+nhzncaqL9HVL6KkHJ28lw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/define-data-property": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", @@ -649,6 +1589,14 @@ "node": ">= 0.4" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", @@ -679,59 +1627,50 @@ "node": ">=8" } }, + "node_modules/dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/docker-modem": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-5.0.1.tgz", - "integrity": "sha512-vqrE/nrweCyzmCpVpdFRC41qS+tfTF+IoUKlTZr52O82urbUqdfyJBGWMvT01pYUprWepLr8IkyVTEWJKRTQSg==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-5.0.3.tgz", + "integrity": "sha512-89zhop5YVhcPEt5FpUFGr3cDyceGhq/F9J+ZndQ4KfqNvfbJpPMfgeixFgUj5OjCYAboElqODxY5Z1EBsSa6sg==", "dependencies": { "debug": "^4.1.1", "readable-stream": "^3.5.0", "split-ca": "^1.0.1", - "ssh2": "^1.11.0" + "ssh2": "^1.15.0" }, "engines": { "node": ">= 8.0" } }, "node_modules/dockerode": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/dockerode/-/dockerode-4.0.0.tgz", - "integrity": "sha512-3LF7/3MPz5+9RsUo91rD0MCcx0yxjC9bnbtgtVjOLKyKxlZSJ7/Kk3OPAgARlwlWHqXwAGYhmkAHYx7IwD0tJQ==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/dockerode/-/dockerode-4.0.2.tgz", + "integrity": "sha512-9wM1BVpVMFr2Pw3eJNXrYYt6DT9k0xMcsSCjtPvyQ+xa1iPg/Mo3T/gUcwI0B2cczqCeCYRPF8yFYDwtFXT0+w==", "dependencies": { "@balena/dockerignore": "^1.0.2", - "docker-modem": "^5.0.0", + "docker-modem": "^5.0.3", "tar-fs": "~2.0.1" }, "engines": { "node": ">= 8.0" } }, - "node_modules/dockerode-compose": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/dockerode-compose/-/dockerode-compose-1.4.0.tgz", - "integrity": "sha512-6x5ZlK06H+cgoTR4ffucqN5kWVvxNvxwTLcHQUZcegCJBEDGrdzXMOEGDMsxbHwiLtLo2dNwG0eZK7B2RfEWSw==", - "dependencies": { - "dockerode": "^4.0.0", - "js-yaml": "^4.0.0", - "tar-fs": "^2.1.1" - } - }, - "node_modules/dockerode-compose/node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" - }, - "node_modules/dockerode-compose/node_modules/tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, "node_modules/dottie": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.6.tgz", @@ -756,6 +1695,20 @@ "node": ">=0.10.0" } }, + "node_modules/electron-to-chromium": { + "version": "1.4.623", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.623.tgz", + "integrity": "sha512-lKoz10iCYlP1WtRYdh5MvocQPWVRoI7ysp6qf18bmeBgR8abE6+I2CsfyNKztRDZvhdWc+krKT6wS7Neg8sw3A==", + "peer": true + }, + "node_modules/emitter-component": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/emitter-component/-/emitter-component-1.1.2.tgz", + "integrity": "sha512-QdXO3nXOzZB4pAjM0n6ZE+R9/+kPpECA/XSELIcc54NeYVnBqIk+4DFiBgK+8QbV3mdvTG6nedl7dTYgO+5wDw==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -849,11 +1802,30 @@ "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", "optional": true }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -862,6 +1834,14 @@ "node": ">= 0.6" } }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "engines": { + "node": ">=6" + } + }, "node_modules/express": { "version": "4.18.2", "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", @@ -942,6 +1922,25 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/express-session/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/express/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -955,6 +1954,35 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/express/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, "node_modules/filelist": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", @@ -982,6 +2010,17 @@ "node": ">=10" } }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/finalhandler": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", @@ -1012,6 +2051,89 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/find-cache-dir/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/find-cache-dir/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/formidable": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", + "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", + "dependencies": { + "dezalgo": "^1.0.4", + "hexoid": "^1.0.0", + "once": "^1.4.0", + "qs": "^6.11.0" + }, + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -1049,6 +2171,19 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -1076,6 +2211,31 @@ "node": ">=10" } }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "engines": { + "node": "*" + } + }, "node_modules/get-intrinsic": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", @@ -1090,6 +2250,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -1109,6 +2274,26 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "peer": true, + "engines": { + "node": ">=4" + } + }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -1183,6 +2368,14 @@ "node": ">= 0.4" } }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "bin": { + "he": "bin/he" + } + }, "node_modules/helmet": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/helmet/-/helmet-7.1.0.tgz", @@ -1191,6 +2384,14 @@ "node": ">=16.0.0" } }, + "node_modules/hexoid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", + "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", + "engines": { + "node": ">=8" + } + }, "node_modules/http-cache-semantics": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", @@ -1323,6 +2524,11 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, "node_modules/ip": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", @@ -1337,6 +2543,25 @@ "node": ">= 0.10" } }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -1345,18 +2570,80 @@ "node": ">=8" } }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-lambda": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", "optional": true }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "optional": true }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/jake": { "version": "10.8.7", "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", @@ -1374,6 +2661,12 @@ "node": ">=10" } }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "peer": true + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -1385,11 +2678,90 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "peer": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "peer": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/just-extend": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", + "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==" + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/loupe": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.0.2.tgz", + "integrity": "sha512-Tzlkbynv7dtqxTROe54Il+J4e/zG2iehtJGZUYpTv8WzlkW9qyEcE83UhGJCeuF3SCfzHuM5VWhBi47phV3+AQ==", + "dependencies": { + "get-func-name": "^2.0.1" + } + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -1501,6 +2873,17 @@ "node": ">= 0.6" } }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -1512,6 +2895,14 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/minipass": { "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", @@ -1616,18 +3007,136 @@ "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" }, + "node_modules/mocha": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", + "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", + "dependencies": { + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "nanoid": "3.3.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/minimatch/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/mocha/node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", "engines": { "node": "*" } }, "node_modules/moment-timezone": { - "version": "0.5.43", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.43.tgz", - "integrity": "sha512-72j3aNyuIsDxdF1i7CEgV2FfxM1r6aaqJyLB2vwb33mXYyoyLly+F1zbWqhA3/bVIoJ4szlUoMbUnVdid32NUQ==", + "version": "0.5.44", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.44.tgz", + "integrity": "sha512-nv3YpzI/8lkQn0U6RkLd+f0W/zy/JnoR5/EyPz/dNkPTBjA2jNLCVxaiQ8QpeLymhSZvX0wCL5s27NQWdOPwAw==", "dependencies": { "moment": "^2.29.4" }, @@ -1646,6 +3155,22 @@ "integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==", "optional": true }, + "node_modules/nanoid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==" + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -1654,10 +3179,65 @@ "node": ">= 0.6" } }, + "node_modules/nise": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.5.tgz", + "integrity": "sha512-VJuPIfUFaXNRzETTQEEItTOP8Y171ijr+JLq42wHes3DiryR8vT+1TXQW/Rx8JNUhyYYWyIvjXTU6dOhJcs9Nw==", + "dependencies": { + "@sinonjs/commons": "^2.0.0", + "@sinonjs/fake-timers": "^10.0.2", + "@sinonjs/text-encoding": "^0.7.1", + "just-extend": "^4.0.2", + "path-to-regexp": "^1.7.0" + } + }, + "node_modules/nise/node_modules/@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/nise/node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/nise/node_modules/@sinonjs/fake-timers/node_modules/@sinonjs/commons": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/nise/node_modules/path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "dependencies": { + "isarray": "0.0.1" + } + }, + "node_modules/node-abi": { + "version": "3.54.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.54.0.tgz", + "integrity": "sha512-p7eGEiQil0YUV3ItH4/tBb781L5impVmmx2E9FRKF7d18XXzp4PGT2tdYMFY6wQqgxD0IwNZOiSJ0/K0fSi/OA==", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/node-addon-api": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", - "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==" }, "node_modules/node-fetch": { "version": "2.7.0", @@ -1749,6 +3329,12 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "peer": true + }, "node_modules/nopt": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", @@ -1763,6 +3349,14 @@ "node": ">=6" } }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/npmlog": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", @@ -1817,6 +3411,34 @@ "wrappy": "1" } }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-map": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", @@ -1832,6 +3454,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "engines": { + "node": ">=6" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -1840,6 +3470,14 @@ "node": ">= 0.8" } }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "engines": { + "node": ">=8" + } + }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -1853,11 +3491,144 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, + "node_modules/pathval": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", + "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", + "engines": { + "node": ">= 14.16" + } + }, "node_modules/pg-connection-string": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "peer": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "engines": { + "node": ">=6" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-dir/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/prebuild-install": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", + "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/promise-inflight": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", @@ -1920,6 +3691,14 @@ "node": ">= 0.8" } }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -1942,6 +3721,28 @@ "node": ">= 0.8" } }, + "node_modules/raw-body/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -1955,6 +3756,25 @@ "node": ">= 6" } }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/retry": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", @@ -1984,23 +3804,9 @@ } }, "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "node_modules/safer-buffer": { "version": "2.1.2", @@ -2131,6 +3937,14 @@ "node": ">= 10.0.0" } }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dependencies": { + "randombytes": "^2.1.0" + } + }, "node_modules/serve-static": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", @@ -2169,6 +3983,17 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -2187,6 +4012,74 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/sinon": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-17.0.1.tgz", + "integrity": "sha512-wmwE19Lie0MLT+ZYNpDymasPHUKTaZHUH/pKEubRXIzySv9Atnlw+BUMGCzWgV7b7wO+Hw6f1TEOr0IUnmU8/g==", + "dependencies": { + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^11.2.2", + "@sinonjs/samsam": "^8.0.0", + "diff": "^5.1.0", + "nise": "^5.1.5", + "supports-color": "^7.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } + }, + "node_modules/sinon/node_modules/diff": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", @@ -2198,9 +4091,9 @@ } }, "node_modules/socket.io": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.2.tgz", - "integrity": "sha512-bvKVS29/I5fl2FGLNHuXlQaUH/BlzX1IN6S+NKLNZpBsPZIDH+90eQmCs2Railn4YUiww4SzUedJ6+uzwFnKLw==", + "version": "4.7.3", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.3.tgz", + "integrity": "sha512-SE+UIQXBQE+GPG2oszWMlsEmWtHVqw/h1VrYJGK5/MC7CH5p58N448HwIrtREcvR4jfdOJAY4ieQfxMr55qbbw==", "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", @@ -2262,19 +4155,37 @@ "node": ">= 10" } }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "node_modules/split-ca": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz", "integrity": "sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==" }, "node_modules/sqlite3": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.6.tgz", - "integrity": "sha512-olYkWoKFVNSSSQNvxVUfjiVbz3YtBwTJj+mfV5zpHmqW3sELx2Cf4QCdirMelhM5Zh+KDVaKgQHqCxrqiWHybw==", + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.7.tgz", + "integrity": "sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==", "hasInstallScript": true, "dependencies": { - "@mapbox/node-pre-gyp": "^1.0.0", - "node-addon-api": "^4.2.0", + "bindings": "^1.5.0", + "node-addon-api": "^7.0.0", + "prebuild-install": "^7.1.1", "tar": "^6.1.11" }, "optionalDependencies": { @@ -2290,14 +4201,14 @@ } }, "node_modules/sqlite3/node_modules/node-addon-api": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", - "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==" + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.0.0.tgz", + "integrity": "sha512-vgbBJTS4m5/KkE16t5Ly0WW9hz46swAstv0hYYwMtbG7AznRhNyfLRe8HZAiWIpcHzoO7HxhLuBQj9rJ/Ho0ZA==" }, "node_modules/ssh2": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.14.0.tgz", - "integrity": "sha512-AqzD1UCqit8tbOKoj6ztDDi1ffJZ2rV2SwlgrVVrHPkV5vWqGJOVp5pmtj18PunkPJAuKQsnInyKV+/Nb2bUnA==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.15.0.tgz", + "integrity": "sha512-C0PHgX4h6lBxYx7hcXwu3QWdh4tg6tZZsTfXcdvc5caW/EMxaB4H9dWsl7qk+F7LAW762hp8VbXOX7x4xUYvEw==", "hasInstallScript": true, "dependencies": { "asn1": "^0.2.6", @@ -2307,8 +4218,8 @@ "node": ">=10.16.0" }, "optionalDependencies": { - "cpu-features": "~0.0.8", - "nan": "^2.17.0" + "cpu-features": "~0.0.9", + "nan": "^2.18.0" } }, "node_modules/ssri": { @@ -2331,6 +4242,14 @@ "node": ">= 0.8" } }, + "node_modules/stream": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stream/-/stream-0.0.2.tgz", + "integrity": "sha512-gCq3NDI2P35B2n6t76YJuOp7d6cN/C7Rt0577l91wllh0sY9ZBuw9KaSGqH/b0hzn3CWWJbpbW0W0WvQ1H/Q7g==", + "dependencies": { + "emitter-component": "^1.1.1" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -2339,6 +4258,25 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -2363,6 +4301,57 @@ "node": ">=8" } }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/superagent": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.1.2.tgz", + "integrity": "sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA==", + "dependencies": { + "component-emitter": "^1.3.0", + "cookiejar": "^2.1.4", + "debug": "^4.3.4", + "fast-safe-stringify": "^2.1.1", + "form-data": "^4.0.0", + "formidable": "^2.1.2", + "methods": "^1.1.2", + "mime": "2.6.0", + "qs": "^6.11.0", + "semver": "^7.3.8" + }, + "engines": { + "node": ">=6.4.0 <13 || >=14" + } + }, + "node_modules/superagent/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/supertest": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.3.3.tgz", + "integrity": "sha512-EMCG6G8gDu5qEqRQ3JjjPs6+FYT1a7Hv5ApHvtSghmOFJYtsU5S+pSb6Y2EUeCEY3CmEL3mmQ8YWlPOzQomabA==", + "dependencies": { + "methods": "^1.1.2", + "superagent": "^8.0.5" + }, + "engines": { + "node": ">=6.4.0" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -2375,9 +4364,9 @@ } }, "node_modules/systeminformation": { - "version": "5.21.20", - "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.21.20.tgz", - "integrity": "sha512-AyS1fNc+MDoAJtFknFbbo587H8h6yejJwM+H9rVusnOToIEkiMehMyD5JM7o3j55Cto20MawIZrcgNMgd4BfOQ==", + "version": "5.21.22", + "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.21.22.tgz", + "integrity": "sha512-gNHloAJSyS+sKWkwvmvozZ1eHrdVTEsynWMTY6lvLGBB70gflkBQFw8drXXr1oEXY84+Vr9tOOrN8xHZLJSycA==", "os": [ "darwin", "linux", @@ -2454,6 +4443,26 @@ "node": ">=8" } }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -2472,11 +4481,30 @@ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "engines": { + "node": ">=4" + } + }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -2531,6 +4559,36 @@ "node": ">= 0.8" } }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "peer": true, + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -2613,6 +4671,27 @@ "@types/node": "*" } }, + "node_modules/workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -2638,10 +4717,68 @@ } } }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/package.json b/package.json index 8bd93e6..cf9f02d 100644 --- a/package.json +++ b/package.json @@ -1,25 +1,35 @@ { "name": "dweebui", "version": "1.0.0", + "description": "A web UI for Docker", "main": "app.js", + "type": "module", + "scripts": { + "test": "mocha --require @babel/register --exit" + }, "keywords": [], "author": "", "license": "ISC", "dependencies": { - "bcrypt": "^5.1.0", - "child_process": "^1.0.2", + "@babel/register": "^7.23.7", + "@journeyapps/sqlcipher": "^5.3.1", + "@socket.io/admin-ui": "^0.5.1", + "bcrypt": "^5.1.1", + "chai": "^5.0.0", "compression": "^1.7.4", - "dockerode": "^4.0.0", - "dockerode-compose": "^1.4.0", + "cors": "^2.8.5", + "dockerode": "^4.0.1", "ejs": "^3.1.9", "express": "^4.18.2", "express-session": "^1.17.3", "helmet": "^7.1.0", - "js-yaml": "^4.1.0", + "mocha": "^10.2.0", "sequelize": "^6.35.2", - "socket.io": "^4.6.1", + "sinon": "^17.0.1", + "socket.io": "^4.7.2", "sqlite3": "^5.1.6", - "systeminformation": "^5.21.20" - }, - "description": "" + "stream": "^0.0.2", + "supertest": "^6.3.3", + "systeminformation": "^5.21.22" + } } diff --git a/public/css/meters.css b/public/css/meters.css index 6f97f47..89ded5b 100644 --- a/public/css/meters.css +++ b/public/css/meters.css @@ -2,7 +2,7 @@ .meter { box-sizing: content-box; - height: 15px; /* Can be anything */ + height: 15px; margin-left: auto; margin-right: auto; position: relative; diff --git a/public/css/tabler.min.css b/public/css/tabler.min.css index 5c288bd..3c0f155 100644 --- a/public/css/tabler.min.css +++ b/public/css/tabler.min.css @@ -11462,47 +11462,38 @@ fieldset:disabled .btn { } .column-gap-0 { - -moz-column-gap: 0 !important; column-gap: 0 !important } .column-gap-1 { - -moz-column-gap: .25rem !important; column-gap: .25rem !important } .column-gap-2 { - -moz-column-gap: .5rem !important; column-gap: .5rem !important } .column-gap-3 { - -moz-column-gap: 1rem !important; column-gap: 1rem !important } .column-gap-4 { - -moz-column-gap: 1.5rem !important; column-gap: 1.5rem !important } .column-gap-5 { - -moz-column-gap: 2rem !important; column-gap: 2rem !important } .column-gap-6 { - -moz-column-gap: 3rem !important; column-gap: 3rem !important } .column-gap-7 { - -moz-column-gap: 5rem !important; column-gap: 5rem !important } .column-gap-8 { - -moz-column-gap: 8rem !important; column-gap: 8rem !important } @@ -12910,17 +12901,14 @@ fieldset:disabled .btn { } .columns-2 { - -moz-columns: 2 !important; columns: 2 !important } .columns-3 { - -moz-columns: 3 !important; columns: 3 !important } .columns-4 { - -moz-columns: 4 !important; columns: 4 !important } @@ -13821,47 +13809,38 @@ fieldset:disabled .btn { } .column-gap-sm-0 { - -moz-column-gap: 0 !important; column-gap: 0 !important } .column-gap-sm-1 { - -moz-column-gap: .25rem !important; column-gap: .25rem !important } .column-gap-sm-2 { - -moz-column-gap: .5rem !important; column-gap: .5rem !important } .column-gap-sm-3 { - -moz-column-gap: 1rem !important; column-gap: 1rem !important } .column-gap-sm-4 { - -moz-column-gap: 1.5rem !important; column-gap: 1.5rem !important } .column-gap-sm-5 { - -moz-column-gap: 2rem !important; column-gap: 2rem !important } .column-gap-sm-6 { - -moz-column-gap: 3rem !important; column-gap: 3rem !important } .column-gap-sm-7 { - -moz-column-gap: 5rem !important; column-gap: 5rem !important } .column-gap-sm-8 { - -moz-column-gap: 8rem !important; column-gap: 8rem !important } @@ -13878,17 +13857,14 @@ fieldset:disabled .btn { } .columns-sm-2 { - -moz-columns: 2 !important; columns: 2 !important } .columns-sm-3 { - -moz-columns: 3 !important; columns: 3 !important } .columns-sm-4 { - -moz-columns: 4 !important; columns: 4 !important } } @@ -14790,47 +14766,38 @@ fieldset:disabled .btn { } .column-gap-md-0 { - -moz-column-gap: 0 !important; column-gap: 0 !important } .column-gap-md-1 { - -moz-column-gap: .25rem !important; column-gap: .25rem !important } .column-gap-md-2 { - -moz-column-gap: .5rem !important; column-gap: .5rem !important } .column-gap-md-3 { - -moz-column-gap: 1rem !important; column-gap: 1rem !important } .column-gap-md-4 { - -moz-column-gap: 1.5rem !important; column-gap: 1.5rem !important } .column-gap-md-5 { - -moz-column-gap: 2rem !important; column-gap: 2rem !important } .column-gap-md-6 { - -moz-column-gap: 3rem !important; column-gap: 3rem !important } .column-gap-md-7 { - -moz-column-gap: 5rem !important; column-gap: 5rem !important } .column-gap-md-8 { - -moz-column-gap: 8rem !important; column-gap: 8rem !important } @@ -14847,17 +14814,14 @@ fieldset:disabled .btn { } .columns-md-2 { - -moz-columns: 2 !important; columns: 2 !important } .columns-md-3 { - -moz-columns: 3 !important; columns: 3 !important } .columns-md-4 { - -moz-columns: 4 !important; columns: 4 !important } } @@ -15759,47 +15723,38 @@ fieldset:disabled .btn { } .column-gap-lg-0 { - -moz-column-gap: 0 !important; column-gap: 0 !important } .column-gap-lg-1 { - -moz-column-gap: .25rem !important; column-gap: .25rem !important } .column-gap-lg-2 { - -moz-column-gap: .5rem !important; column-gap: .5rem !important } .column-gap-lg-3 { - -moz-column-gap: 1rem !important; column-gap: 1rem !important } .column-gap-lg-4 { - -moz-column-gap: 1.5rem !important; column-gap: 1.5rem !important } .column-gap-lg-5 { - -moz-column-gap: 2rem !important; column-gap: 2rem !important } .column-gap-lg-6 { - -moz-column-gap: 3rem !important; column-gap: 3rem !important } .column-gap-lg-7 { - -moz-column-gap: 5rem !important; column-gap: 5rem !important } .column-gap-lg-8 { - -moz-column-gap: 8rem !important; column-gap: 8rem !important } @@ -15816,17 +15771,14 @@ fieldset:disabled .btn { } .columns-lg-2 { - -moz-columns: 2 !important; columns: 2 !important } .columns-lg-3 { - -moz-columns: 3 !important; columns: 3 !important } .columns-lg-4 { - -moz-columns: 4 !important; columns: 4 !important } } @@ -16728,47 +16680,38 @@ fieldset:disabled .btn { } .column-gap-xl-0 { - -moz-column-gap: 0 !important; column-gap: 0 !important } .column-gap-xl-1 { - -moz-column-gap: .25rem !important; column-gap: .25rem !important } .column-gap-xl-2 { - -moz-column-gap: .5rem !important; column-gap: .5rem !important } .column-gap-xl-3 { - -moz-column-gap: 1rem !important; column-gap: 1rem !important } .column-gap-xl-4 { - -moz-column-gap: 1.5rem !important; column-gap: 1.5rem !important } .column-gap-xl-5 { - -moz-column-gap: 2rem !important; column-gap: 2rem !important } .column-gap-xl-6 { - -moz-column-gap: 3rem !important; column-gap: 3rem !important } .column-gap-xl-7 { - -moz-column-gap: 5rem !important; column-gap: 5rem !important } .column-gap-xl-8 { - -moz-column-gap: 8rem !important; column-gap: 8rem !important } @@ -16785,17 +16728,14 @@ fieldset:disabled .btn { } .columns-xl-2 { - -moz-columns: 2 !important; columns: 2 !important } .columns-xl-3 { - -moz-columns: 3 !important; columns: 3 !important } .columns-xl-4 { - -moz-columns: 4 !important; columns: 4 !important } } @@ -17697,47 +17637,38 @@ fieldset:disabled .btn { } .column-gap-xxl-0 { - -moz-column-gap: 0 !important; column-gap: 0 !important } .column-gap-xxl-1 { - -moz-column-gap: .25rem !important; column-gap: .25rem !important } .column-gap-xxl-2 { - -moz-column-gap: .5rem !important; column-gap: .5rem !important } .column-gap-xxl-3 { - -moz-column-gap: 1rem !important; column-gap: 1rem !important } .column-gap-xxl-4 { - -moz-column-gap: 1.5rem !important; column-gap: 1.5rem !important } .column-gap-xxl-5 { - -moz-column-gap: 2rem !important; column-gap: 2rem !important } .column-gap-xxl-6 { - -moz-column-gap: 3rem !important; column-gap: 3rem !important } .column-gap-xxl-7 { - -moz-column-gap: 5rem !important; column-gap: 5rem !important } .column-gap-xxl-8 { - -moz-column-gap: 8rem !important; column-gap: 8rem !important } @@ -17754,17 +17685,14 @@ fieldset:disabled .btn { } .columns-xxl-2 { - -moz-columns: 2 !important; columns: 2 !important } .columns-xxl-3 { - -moz-columns: 3 !important; columns: 3 !important } .columns-xxl-4 { - -moz-columns: 4 !important; columns: 4 !important } } diff --git a/public/fonts/Inter-Black.woff2 b/public/fonts/Inter-Black.woff2 new file mode 100644 index 0000000..18b35db Binary files /dev/null and b/public/fonts/Inter-Black.woff2 differ diff --git a/public/fonts/Inter-BlackItalic.woff2 b/public/fonts/Inter-BlackItalic.woff2 new file mode 100644 index 0000000..02c9d8e Binary files /dev/null and b/public/fonts/Inter-BlackItalic.woff2 differ diff --git a/public/fonts/Inter-Bold.woff2 b/public/fonts/Inter-Bold.woff2 new file mode 100644 index 0000000..0f1b157 Binary files /dev/null and b/public/fonts/Inter-Bold.woff2 differ diff --git a/public/fonts/Inter-BoldItalic.woff2 b/public/fonts/Inter-BoldItalic.woff2 new file mode 100644 index 0000000..bc50f24 Binary files /dev/null and b/public/fonts/Inter-BoldItalic.woff2 differ diff --git a/public/fonts/Inter-ExtraBold.woff2 b/public/fonts/Inter-ExtraBold.woff2 new file mode 100644 index 0000000..b113368 Binary files /dev/null and b/public/fonts/Inter-ExtraBold.woff2 differ diff --git a/public/fonts/Inter-ExtraBoldItalic.woff2 b/public/fonts/Inter-ExtraBoldItalic.woff2 new file mode 100644 index 0000000..a5b76ca Binary files /dev/null and b/public/fonts/Inter-ExtraBoldItalic.woff2 differ diff --git a/public/fonts/Inter-ExtraLight.woff2 b/public/fonts/Inter-ExtraLight.woff2 new file mode 100644 index 0000000..1d77ae8 Binary files /dev/null and b/public/fonts/Inter-ExtraLight.woff2 differ diff --git a/public/fonts/Inter-ExtraLightItalic.woff2 b/public/fonts/Inter-ExtraLightItalic.woff2 new file mode 100644 index 0000000..8c68492 Binary files /dev/null and b/public/fonts/Inter-ExtraLightItalic.woff2 differ diff --git a/public/fonts/Inter-Italic.woff2 b/public/fonts/Inter-Italic.woff2 new file mode 100644 index 0000000..4c24ce2 Binary files /dev/null and b/public/fonts/Inter-Italic.woff2 differ diff --git a/public/fonts/Inter-Light.woff2 b/public/fonts/Inter-Light.woff2 new file mode 100644 index 0000000..dbe6143 Binary files /dev/null and b/public/fonts/Inter-Light.woff2 differ diff --git a/public/fonts/Inter-LightItalic.woff2 b/public/fonts/Inter-LightItalic.woff2 new file mode 100644 index 0000000..a40d042 Binary files /dev/null and b/public/fonts/Inter-LightItalic.woff2 differ diff --git a/public/fonts/Inter-Medium.woff2 b/public/fonts/Inter-Medium.woff2 new file mode 100644 index 0000000..0fd2ee7 Binary files /dev/null and b/public/fonts/Inter-Medium.woff2 differ diff --git a/public/fonts/Inter-MediumItalic.woff2 b/public/fonts/Inter-MediumItalic.woff2 new file mode 100644 index 0000000..9676715 Binary files /dev/null and b/public/fonts/Inter-MediumItalic.woff2 differ diff --git a/public/fonts/Inter-Regular.woff2 b/public/fonts/Inter-Regular.woff2 new file mode 100644 index 0000000..b8699af Binary files /dev/null and b/public/fonts/Inter-Regular.woff2 differ diff --git a/public/fonts/Inter-SemiBold.woff2 b/public/fonts/Inter-SemiBold.woff2 new file mode 100644 index 0000000..95c48b1 Binary files /dev/null and b/public/fonts/Inter-SemiBold.woff2 differ diff --git a/public/fonts/Inter-SemiBoldItalic.woff2 b/public/fonts/Inter-SemiBoldItalic.woff2 new file mode 100644 index 0000000..ddfe19e Binary files /dev/null and b/public/fonts/Inter-SemiBoldItalic.woff2 differ diff --git a/public/fonts/Inter-Thin.woff2 b/public/fonts/Inter-Thin.woff2 new file mode 100644 index 0000000..0790960 Binary files /dev/null and b/public/fonts/Inter-Thin.woff2 differ diff --git a/public/fonts/Inter-ThinItalic.woff2 b/public/fonts/Inter-ThinItalic.woff2 new file mode 100644 index 0000000..a7bf213 Binary files /dev/null and b/public/fonts/Inter-ThinItalic.woff2 differ diff --git a/public/fonts/InterDisplay-Black.woff2 b/public/fonts/InterDisplay-Black.woff2 new file mode 100644 index 0000000..8138123 Binary files /dev/null and b/public/fonts/InterDisplay-Black.woff2 differ diff --git a/public/fonts/InterDisplay-BlackItalic.woff2 b/public/fonts/InterDisplay-BlackItalic.woff2 new file mode 100644 index 0000000..735ba21 Binary files /dev/null and b/public/fonts/InterDisplay-BlackItalic.woff2 differ diff --git a/public/fonts/InterDisplay-Bold.woff2 b/public/fonts/InterDisplay-Bold.woff2 new file mode 100644 index 0000000..11c6719 Binary files /dev/null and b/public/fonts/InterDisplay-Bold.woff2 differ diff --git a/public/fonts/InterDisplay-BoldItalic.woff2 b/public/fonts/InterDisplay-BoldItalic.woff2 new file mode 100644 index 0000000..5b6a1fb Binary files /dev/null and b/public/fonts/InterDisplay-BoldItalic.woff2 differ diff --git a/public/fonts/InterDisplay-ExtraBold.woff2 b/public/fonts/InterDisplay-ExtraBold.woff2 new file mode 100644 index 0000000..9058e98 Binary files /dev/null and b/public/fonts/InterDisplay-ExtraBold.woff2 differ diff --git a/public/fonts/InterDisplay-ExtraBoldItalic.woff2 b/public/fonts/InterDisplay-ExtraBoldItalic.woff2 new file mode 100644 index 0000000..4cd61c0 Binary files /dev/null and b/public/fonts/InterDisplay-ExtraBoldItalic.woff2 differ diff --git a/public/fonts/InterDisplay-ExtraLight.woff2 b/public/fonts/InterDisplay-ExtraLight.woff2 new file mode 100644 index 0000000..8621b29 Binary files /dev/null and b/public/fonts/InterDisplay-ExtraLight.woff2 differ diff --git a/public/fonts/InterDisplay-ExtraLightItalic.woff2 b/public/fonts/InterDisplay-ExtraLightItalic.woff2 new file mode 100644 index 0000000..689c8d9 Binary files /dev/null and b/public/fonts/InterDisplay-ExtraLightItalic.woff2 differ diff --git a/public/fonts/InterDisplay-Italic.woff2 b/public/fonts/InterDisplay-Italic.woff2 new file mode 100644 index 0000000..11f20bc Binary files /dev/null and b/public/fonts/InterDisplay-Italic.woff2 differ diff --git a/public/fonts/InterDisplay-Light.woff2 b/public/fonts/InterDisplay-Light.woff2 new file mode 100644 index 0000000..446301c Binary files /dev/null and b/public/fonts/InterDisplay-Light.woff2 differ diff --git a/public/fonts/InterDisplay-LightItalic.woff2 b/public/fonts/InterDisplay-LightItalic.woff2 new file mode 100644 index 0000000..f688196 Binary files /dev/null and b/public/fonts/InterDisplay-LightItalic.woff2 differ diff --git a/public/fonts/InterDisplay-Medium.woff2 b/public/fonts/InterDisplay-Medium.woff2 new file mode 100644 index 0000000..29160b2 Binary files /dev/null and b/public/fonts/InterDisplay-Medium.woff2 differ diff --git a/public/fonts/InterDisplay-MediumItalic.woff2 b/public/fonts/InterDisplay-MediumItalic.woff2 new file mode 100644 index 0000000..ef1bcbe Binary files /dev/null and b/public/fonts/InterDisplay-MediumItalic.woff2 differ diff --git a/public/fonts/InterDisplay-Regular.woff2 b/public/fonts/InterDisplay-Regular.woff2 new file mode 100644 index 0000000..a6c04f6 Binary files /dev/null and b/public/fonts/InterDisplay-Regular.woff2 differ diff --git a/public/fonts/InterDisplay-SemiBold.woff2 b/public/fonts/InterDisplay-SemiBold.woff2 new file mode 100644 index 0000000..2b4db23 Binary files /dev/null and b/public/fonts/InterDisplay-SemiBold.woff2 differ diff --git a/public/fonts/InterDisplay-SemiBoldItalic.woff2 b/public/fonts/InterDisplay-SemiBoldItalic.woff2 new file mode 100644 index 0000000..59091db Binary files /dev/null and b/public/fonts/InterDisplay-SemiBoldItalic.woff2 differ diff --git a/public/fonts/InterDisplay-Thin.woff2 b/public/fonts/InterDisplay-Thin.woff2 new file mode 100644 index 0000000..dc0b948 Binary files /dev/null and b/public/fonts/InterDisplay-Thin.woff2 differ diff --git a/public/fonts/InterDisplay-ThinItalic.woff2 b/public/fonts/InterDisplay-ThinItalic.woff2 new file mode 100644 index 0000000..96439c0 Binary files /dev/null and b/public/fonts/InterDisplay-ThinItalic.woff2 differ diff --git a/public/fonts/InterVariable-Italic.woff2 b/public/fonts/InterVariable-Italic.woff2 new file mode 100644 index 0000000..f22ec25 Binary files /dev/null and b/public/fonts/InterVariable-Italic.woff2 differ diff --git a/public/fonts/InterVariable.woff2 b/public/fonts/InterVariable.woff2 new file mode 100644 index 0000000..22a12b0 Binary files /dev/null and b/public/fonts/InterVariable.woff2 differ diff --git a/public/fonts/inter.css b/public/fonts/inter.css new file mode 100644 index 0000000..ff5b47d --- /dev/null +++ b/public/fonts/inter.css @@ -0,0 +1,59 @@ +/* Variable fonts usage: +:root { font-family: "Inter", sans-serif; } +@supports (font-variation-settings: normal) { + :root { font-family: "InterVariable", sans-serif; font-optical-sizing: auto; } +} */ +@font-face { + font-family: InterVariable; + font-style: normal; + font-weight: 100 900; + font-display: swap; + src: url('InterVariable.woff2?v=4.0') format('woff2'); +} +@font-face { + font-family: InterVariable; + font-style: italic; + font-weight: 100 900; + font-display: swap; + src: url('InterVariable-Italic.woff2?v=4.0') format('woff2'); +} +/* legacy name "Inter var" (Oct 2023) */ +@font-face { font-family:'Inter var'; font-style:normal; font-weight:100 900; font-display:swap; src: url('InterVariable.woff2?v=4.0') format('woff2'); } +@font-face { font-family:'Inter var'; font-style:italic; font-weight:100 900; font-display:swap; src: url('InterVariable-Italic.woff2?v=4.0') format('woff2'); } +/* static fonts */ +@font-face { font-family:Inter; font-style:normal; font-weight:100; font-display:swap; src:url("Inter-Thin.woff2?v=4.0") format("woff2"); } +@font-face { font-family:Inter; font-style:italic; font-weight:100; font-display:swap; src:url("Inter-ThinItalic.woff2?v=4.0") format("woff2"); } +@font-face { font-family:Inter; font-style:normal; font-weight:200; font-display:swap; src:url("Inter-ExtraLight.woff2?v=4.0") format("woff2"); } +@font-face { font-family:Inter; font-style:italic; font-weight:200; font-display:swap; src:url("Inter-ExtraLightItalic.woff2?v=4.0") format("woff2"); } +@font-face { font-family:Inter; font-style:normal; font-weight:300; font-display:swap; src:url("Inter-Light.woff2?v=4.0") format("woff2"); } +@font-face { font-family:Inter; font-style:italic; font-weight:300; font-display:swap; src:url("Inter-LightItalic.woff2?v=4.0") format("woff2"); } +@font-face { font-family:Inter; font-style:normal; font-weight:400; font-display:swap; src:url("Inter-Regular.woff2?v=4.0") format("woff2"); } +@font-face { font-family:Inter; font-style:italic; font-weight:400; font-display:swap; src:url("Inter-Italic.woff2?v=4.0") format("woff2"); } +@font-face { font-family:Inter; font-style:normal; font-weight:500; font-display:swap; src:url("Inter-Medium.woff2?v=4.0") format("woff2"); } +@font-face { font-family:Inter; font-style:italic; font-weight:500; font-display:swap; src:url("Inter-MediumItalic.woff2?v=4.0") format("woff2"); } +@font-face { font-family:Inter; font-style:normal; font-weight:600; font-display:swap; src:url("Inter-SemiBold.woff2?v=4.0") format("woff2"); } +@font-face { font-family:Inter; font-style:italic; font-weight:600; font-display:swap; src:url("Inter-SemiBoldItalic.woff2?v=4.0") format("woff2"); } +@font-face { font-family:Inter; font-style:normal; font-weight:700; font-display:swap; src:url("Inter-Bold.woff2?v=4.0") format("woff2"); } +@font-face { font-family:Inter; font-style:italic; font-weight:700; font-display:swap; src:url("Inter-BoldItalic.woff2?v=4.0") format("woff2"); } +@font-face { font-family:Inter; font-style:normal; font-weight:800; font-display:swap; src:url("Inter-ExtraBold.woff2?v=4.0") format("woff2"); } +@font-face { font-family:Inter; font-style:italic; font-weight:800; font-display:swap; src:url("Inter-ExtraBoldItalic.woff2?v=4.0") format("woff2"); } +@font-face { font-family:Inter; font-style:normal; font-weight:900; font-display:swap; src:url("Inter-Black.woff2?v=4.0") format("woff2"); } +@font-face { font-family:Inter; font-style:italic; font-weight:900; font-display:swap; src:url("Inter-BlackItalic.woff2?v=4.0") format("woff2"); } +@font-face { font-family:InterDisplay; font-style:normal; font-weight:100; font-display:swap; src:url("InterDisplay-Thin.woff2?v=4.0") format("woff2"); } +@font-face { font-family:InterDisplay; font-style:italic; font-weight:100; font-display:swap; src:url("InterDisplay-ThinItalic.woff2?v=4.0") format("woff2"); } +@font-face { font-family:InterDisplay; font-style:normal; font-weight:200; font-display:swap; src:url("InterDisplay-ExtraLight.woff2?v=4.0") format("woff2"); } +@font-face { font-family:InterDisplay; font-style:italic; font-weight:200; font-display:swap; src:url("InterDisplay-ExtraLightItalic.woff2?v=4.0") format("woff2"); } +@font-face { font-family:InterDisplay; font-style:normal; font-weight:300; font-display:swap; src:url("InterDisplay-Light.woff2?v=4.0") format("woff2"); } +@font-face { font-family:InterDisplay; font-style:italic; font-weight:300; font-display:swap; src:url("InterDisplay-LightItalic.woff2?v=4.0") format("woff2"); } +@font-face { font-family:InterDisplay; font-style:normal; font-weight:400; font-display:swap; src:url("InterDisplay-Regular.woff2?v=4.0") format("woff2"); } +@font-face { font-family:InterDisplay; font-style:italic; font-weight:400; font-display:swap; src:url("InterDisplay-Italic.woff2?v=4.0") format("woff2"); } +@font-face { font-family:InterDisplay; font-style:normal; font-weight:500; font-display:swap; src:url("InterDisplay-Medium.woff2?v=4.0") format("woff2"); } +@font-face { font-family:InterDisplay; font-style:italic; font-weight:500; font-display:swap; src:url("InterDisplay-MediumItalic.woff2?v=4.0") format("woff2"); } +@font-face { font-family:InterDisplay; font-style:normal; font-weight:600; font-display:swap; src:url("InterDisplay-SemiBold.woff2?v=4.0") format("woff2"); } +@font-face { font-family:InterDisplay; font-style:italic; font-weight:600; font-display:swap; src:url("InterDisplay-SemiBoldItalic.woff2?v=4.0") format("woff2"); } +@font-face { font-family:InterDisplay; font-style:normal; font-weight:700; font-display:swap; src:url("InterDisplay-Bold.woff2?v=4.0") format("woff2"); } +@font-face { font-family:InterDisplay; font-style:italic; font-weight:700; font-display:swap; src:url("InterDisplay-BoldItalic.woff2?v=4.0") format("woff2"); } +@font-face { font-family:InterDisplay; font-style:normal; font-weight:800; font-display:swap; src:url("InterDisplay-ExtraBold.woff2?v=4.0") format("woff2"); } +@font-face { font-family:InterDisplay; font-style:italic; font-weight:800; font-display:swap; src:url("InterDisplay-ExtraBoldItalic.woff2?v=4.0") format("woff2"); } +@font-face { font-family:InterDisplay; font-style:normal; font-weight:900; font-display:swap; src:url("InterDisplay-Black.woff2?v=4.0") format("woff2"); } +@font-face { font-family:InterDisplay; font-style:italic; font-weight:900; font-display:swap; src:url("InterDisplay-BlackItalic.woff2?v=4.0") format("woff2"); } \ No newline at end of file diff --git a/public/static/avatars/burns.jpg b/public/img/avatars/burns.jpg similarity index 100% rename from public/static/avatars/burns.jpg rename to public/img/avatars/burns.jpg diff --git a/public/img/avatars/duffman.png b/public/img/avatars/duffman.png new file mode 100644 index 0000000..6b29787 Binary files /dev/null and b/public/img/avatars/duffman.png differ diff --git a/public/static/avatars/frank.jpg b/public/img/avatars/frank.jpg similarity index 100% rename from public/static/avatars/frank.jpg rename to public/img/avatars/frank.jpg diff --git a/public/static/avatars/moe.jpg b/public/img/avatars/moe.jpg similarity index 100% rename from public/static/avatars/moe.jpg rename to public/img/avatars/moe.jpg diff --git a/public/img/avatars/moleman.png b/public/img/avatars/moleman.png new file mode 100644 index 0000000..ed0a8ab Binary files /dev/null and b/public/img/avatars/moleman.png differ diff --git a/public/static/avatars/poochie.jpg b/public/img/avatars/poochie.jpg similarity index 100% rename from public/static/avatars/poochie.jpg rename to public/img/avatars/poochie.jpg diff --git a/public/static/avatars/rus.jpg b/public/img/avatars/rus.jpg similarity index 100% rename from public/static/avatars/rus.jpg rename to public/img/avatars/rus.jpg diff --git a/public/static/avatars/skinner.jpg b/public/img/avatars/skinner.jpg similarity index 100% rename from public/static/avatars/skinner.jpg rename to public/img/avatars/skinner.jpg diff --git a/public/js/demo-theme.js b/public/js/demo-theme.js index aea1ac9..749ccd6 100644 --- a/public/js/demo-theme.js +++ b/public/js/demo-theme.js @@ -12,7 +12,7 @@ })((function () { 'use strict'; var themeStorageKey = "tablerTheme"; - var defaultTheme = "light"; + var defaultTheme = "dark"; var selectedTheme; var params = new Proxy(new URLSearchParams(window.location.search), { get: function get(searchParams, prop) { diff --git a/public/js/main.js b/public/js/main.js index a91ae29..246d739 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -1,16 +1,6 @@ -// SOCKET IO -const socket = io({ - auth: { - token: "abc" - } -}); +socket.on('connect', () => { console.log('connected'); }); -// ON CONNECT EVENT -socket.on('connect', () => { - console.log('Connected'); -}); - -// SELECT METRICS ELEMENTS +// Server metrics const cpuText = document.getElementById('cpu-text'); const cpuBar = document.getElementById('cpu-bar'); const ramText = document.getElementById('ram-text'); @@ -20,19 +10,22 @@ const netBar = document.getElementById('net-bar'); const diskText = document.getElementById('disk-text'); const diskBar = document.getElementById('disk-bar'); +// Container cards const dockerCards = document.getElementById('cards'); +// Container logs const logViewer = document.getElementById('logView'); -//Update usage bars +// Server metrics socket.on('metrics', (data) => { - - let {cpu, ram, tx, rx, disk} = data; + let [cpu, ram, tx, rx, disk] = data; cpuText.innerHTML = `CPU ${cpu} %`; + if (cpu < 7 ) { cpu = 7; } cpuBar.innerHTML = ``; ramText.innerHTML = `RAM ${ram} %`; + if (ram < 7 ) { ram = 7; } ramBar.innerHTML = ``; tx = Math.round(tx / 1024 / 1024); @@ -42,165 +35,117 @@ socket.on('metrics', (data) => { netBar.innerHTML = ``; diskText.innerHTML = `DISK ${disk} %`; + if (disk < 7 ) { disk = 7; } diskBar.innerHTML = ``; }); -function drawCharts(name, cpu_array, ram_array) { - var elements = document.querySelectorAll(`${name}`); - - Array.from(elements).forEach(function(element) { - if (window.ApexCharts) { - new ApexCharts(element, { - chart: { - type: "line", - fontFamily: 'inherit', - height: 40.0, - sparkline: { - enabled: true - }, - animations: { - enabled: false - } - }, - fill: { - opacity: 1 - }, - stroke: { - width: [2, 1], - dashArray: [0, 3], - lineCap: "round", - curve: "smooth" - }, - series: [{ - name: "CPU", - data: cpu_array - }, { - name: "RAM", - data: ram_array - }], - tooltip: { - theme: 'dark' - }, - grid: { - strokeDashArray: 4 - }, - xaxis: { - labels: { - padding: 0 - }, - tooltip: { - enabled: false - }, - type: 'datetime' - }, - yaxis: { - labels: { - padding: 4 - } - }, - labels: [ - '2020-06-20', '2020-06-21', '2020-06-22', '2020-06-23', '2020-06-24', '2020-06-25', '2020-06-26', '2020-06-27', '2020-06-28', '2020-06-29', '2020-06-30', '2020-07-01', '2020-07-02', '2020-07-03', '2020-07-04', '2020-07-05', '2020-07-06', '2020-07-07', '2020-07-08', '2020-07-09', '2020-07-10', '2020-07-11', '2020-07-12', '2020-07-13', '2020-07-14', '2020-07-15', '2020-07-16', '2020-07-17', '2020-07-18', '2020-07-19' - ], - colors: [tabler.getColor("primary"), tabler.getColor("gray-600")], - legend: { - show: false - } - }).render(); - } - }); -} - -// container button actions -function buttonAction(button) { - socket.emit('clicked', {container: button.name, state: button.id, action: button.value}); -} - - -function hideContainer(button) { - socket.emit('hide', {container: button.name}); -} - -function resetView() { - socket.emit('reset'); -} - -let containerLogs; - -function viewLogs(button) { - - if (button.name != 'refresh') { - containerLogs = button.name; - } - - - socket.emit('logs', {container: containerLogs}); -} - -socket.on('logString', (data) => { - logViewer.innerHTML = `
${data}
`; +// Container cards +socket.on('containers', (data) => { + let deleteMeElements = document.querySelectorAll('.deleteme'); + deleteMeElements.forEach((element) => { + element.parentNode.removeChild(element); + }); + dockerCards.insertAdjacentHTML("afterend", data); }); +function drawCharts(name, cpuArray, ramArray) { + let element = document.querySelector(`${name}`); -socket.on('cards', (data) => { - - console.log('cards deleted'); - let deleteMeElements = document.querySelectorAll('.deleteme'); - deleteMeElements.forEach((element) => { - element.parentNode.removeChild(element); - }); - - dockerCards.insertAdjacentHTML("afterend", data); - - // check localStorage for items ending with _cpu and redraw the charts - for (let i = 0; i < localStorage.length; i++) { - if (localStorage.key(i).endsWith('_cpu')) { - let name = localStorage.key(i).split('_')[0]; - let cpu_array = JSON.parse(localStorage.getItem(`${name}_cpu`)); - let ram_array = JSON.parse(localStorage.getItem(`${name}_ram`)); - drawCharts(`#${name}_chart`, cpu_array, ram_array); + let chart = new ApexCharts(element, { + chart: { + type: "line", + height: 40.0, + sparkline: { + enabled: true + }, + animations: { + enabled: false + } + }, + fill: { + opacity: 1 + }, + stroke: { + width: [2, 1], + dashArray: [0, 3], + lineCap: "round", + curve: "smooth" + }, + series: [{ + name: "CPU", + data: cpuArray + }, { + name: "RAM", + data: ramArray + }], + tooltip: { + enabled: false + }, + grid: { + strokeDashArray: 4 + }, + xaxis: { + labels: { + padding: 0 + }, + tooltip: { + enabled: false + } + }, + yaxis: { + labels: { + padding: 4 + } + }, + colors: [tabler.getColor("primary"), tabler.getColor("gray-600")], + legend: { + show: false } - } - - -}); + }) + chart.render(); +} + +// Buttons functions +function clicked(button) { + socket.emit('clicked', {name: button.name, id: button.id, value: button.value}); +} socket.on('containerStats', (data) => { - - let {name, cpu, ram} = data; - - console.log(`drawing chart for ${name}`) - - var cpu_array = JSON.parse(localStorage.getItem(`${name}_cpu`)); - var ram_array = JSON.parse(localStorage.getItem(`${name}_ram`)); - - if (cpu_array == null) { cpu_array = Array(30).fill(0); } - if (ram_array == null) { ram_array = Array(30).fill(0); } - - cpu_array.push(cpu); - ram_array.push(ram); + let containerStats = data; - cpu_array = cpu_array.slice(-30); - ram_array = ram_array.slice(-30); + for (const [name, statsArray] of Object.entries(containerStats)) { - localStorage.setItem(`${name}_cpu`, JSON.stringify(cpu_array)); - localStorage.setItem(`${name}_ram`, JSON.stringify(ram_array)); + let cpuArray = statsArray.cpuArray; + let ramArray = statsArray.ramArray; - // replace the old chart with the new one - let chart = document.getElementById(`${name}_chart`); - if (chart) { - let newChart = document.createElement('div'); - newChart.id = `${name}_chart`; - chart.parentNode.replaceChild(newChart, chart); - drawCharts(`#${name}_chart`, cpu_array, ram_array); - } else { - console.log(`Chart element with id ${name}_chart not found in the DOM`); + let chart = document.getElementById(`${name}_chart`); + if (chart) { + chart.innerHTML = ''; + drawCharts(`#${name}_chart`, cpuArray, ramArray); + } else { + console.log(`Chart element with id ${name}_chart not found in the DOM`); + } } + }); -socket.on('install', (data) => { - - console.log('added install card'); - dockerCards.insertAdjacentHTML("afterend", data); + + +// socket.on('install', (data) => { +// dockerCards.insertAdjacentHTML("afterend", data); +// }); + +// let containerLogs; + +// function viewLogs(button) { +// if (button.name != 'refresh') { +// containerLogs = button.name; +// } +// socket.emit('logs', {container: containerLogs}); +// } + +socket.on('logs', (data) => { + logViewer.innerHTML = `
${data}
`; }); diff --git a/public/static/logo-dark.svg b/public/static/logo-dark.svg new file mode 100644 index 0000000..02d81dc --- /dev/null +++ b/public/static/logo-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/static/logo-sm-black.svg b/public/static/logo-sm-black.svg new file mode 100644 index 0000000..4664975 --- /dev/null +++ b/public/static/logo-sm-black.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/static/logo-sm-white.svg b/public/static/logo-sm-white.svg new file mode 100644 index 0000000..0a44230 --- /dev/null +++ b/public/static/logo-sm-white.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/static/logo-small-white.svg b/public/static/logo-small-white.svg deleted file mode 100644 index 4d436ef..0000000 --- a/public/static/logo-small-white.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/public/static/logo-small.svg b/public/static/logo-small.svg deleted file mode 100644 index 4b14f51..0000000 --- a/public/static/logo-small.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/static/logo-white.svg b/public/static/logo-white.svg deleted file mode 100644 index 3d6af9a..0000000 --- a/public/static/logo-white.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/public/static/logo.svg b/public/static/logo.svg index 5d395f4..6dfb8a9 100644 --- a/public/static/logo.svg +++ b/public/static/logo.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/router/index.js b/router/index.js new file mode 100644 index 0000000..c00fbaf --- /dev/null +++ b/router/index.js @@ -0,0 +1,60 @@ +import express from "express"; +import { io } from "../app.js"; + +import { Login, submitLogin } from "../controllers/login.js"; +import { Register, submitRegister } from "../controllers/register.js"; +import { Dashboard, searchDashboard } from "../controllers/dashboard.js"; +import { Apps } from "../controllers/apps.js"; +import { Users } from "../controllers/users.js"; +import { Images } from "../controllers/images.js"; +import { Account } from "../controllers/account.js"; +import { Settings } from "../controllers/settings.js"; +import { Networks } from "../controllers/networks.js"; +import { Volumes } from "../controllers/volumes.js"; +import { Syslogs } from "../controllers/syslogs.js"; + +export const router = express.Router(); + +const auth = (req, res, next) => { + if (req.session.role == "admin") { + next(); + } else { + res.redirect("/login"); + } +}; + + +router.get("/login", Login); +router.post("/login", submitLogin); + +router.get("/register", Register); +router.post("/register", submitRegister); + +router.get("/", auth, Dashboard); +router.post("/", auth, searchDashboard); +router.post("/:search", auth, searchDashboard); + +router.get("/apps", auth, Apps); +router.get("/apps/:page", auth, Apps); + +router.get("/users", auth, Users); + +router.get("/images", auth, Images); + +router.get("/networks", auth, Networks); + +router.get("/volumes", auth, Volumes); + +router.get("/syslogs", auth, Syslogs); + +router.get("/account", Account); + +router.get("/settings", auth, Settings); + +router.get("/logout", (req, res) => { + const sessionId = req.session.id; + req.session.destroy(() => { + io.to(sessionId).disconnectSockets(); + res.redirect("/login"); + }); +}); \ No newline at end of file diff --git a/routes/index.js b/routes/index.js deleted file mode 100644 index 79d8411..0000000 --- a/routes/index.js +++ /dev/null @@ -1,59 +0,0 @@ -const express = require("express"); -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 { Images } = require("../controllers/images"); -const { Volumes } = require("../controllers/volumes"); -const { Networks } = require("../controllers/networks"); - -const { Users } = require("../controllers/users"); -const { Account } = require("../controllers/account"); -const { Settings } = require("../controllers/settings"); - -// Authentication middleware -const authenticate = (req, res, next) => { - if (req.session && req.session.role == "admin") { - console.log(`User ${req.session.user} [${req.session.role}] accessed ${req.originalUrl}`) - next(); - } else { - console.log('Not admin') - res.redirect("/login"); - } -}; - -// Dashboard -router.get("/", authenticate, Dashboard); -router.post("/addsite", authenticate, AddSite); -router.post("/removesite", authenticate, RemoveSite); -router.get("/refreshsites", authenticate, RefreshSites); -router.post("/disablesite", authenticate, DisableSite); -router.post("/enablesite", authenticate, EnableSite); - -router.get("/images", authenticate, Images); - -// 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", authenticate, Apps); -router.get("/apps/:page", authenticate, Apps); -router.get("/apps/:template/:page", authenticate, Apps); -router.post("/apps", authenticate, searchApps); - -// Settings page -router.get("/settings", authenticate, Settings); -router.get("/account", authenticate, Account); - -router.post("/install", authenticate, Install); -router.post("/uninstall", authenticate, Uninstall); - -router.get("/users", authenticate, Users); - -module.exports = router; \ No newline at end of file diff --git a/screenshots/account.png b/screenshots/account.png deleted file mode 100644 index 0f2d996..0000000 Binary files a/screenshots/account.png and /dev/null differ diff --git a/screenshots/apps.png b/screenshots/apps.png deleted file mode 100644 index ce86c91..0000000 Binary files a/screenshots/apps.png and /dev/null differ diff --git a/screenshots/dashboard.png b/screenshots/dashboard.png deleted file mode 100644 index 16d8450..0000000 Binary files a/screenshots/dashboard.png and /dev/null differ diff --git a/screenshots/logs.png b/screenshots/logs.png deleted file mode 100644 index 0a9cff3..0000000 Binary files a/screenshots/logs.png and /dev/null differ diff --git a/screenshots/register.png b/screenshots/register.png deleted file mode 100644 index a72e51a..0000000 Binary files a/screenshots/register.png and /dev/null differ diff --git a/setup.sh b/setup.sh deleted file mode 100644 index 7264ca7..0000000 --- a/setup.sh +++ /dev/null @@ -1,70 +0,0 @@ -#!/bin/bash - -# To demo DweebUI, run this script on a fresh Debian 12.2 install. This script will open port 443/tcp for Reverse Proxy and 22/tcp for SSH. - -# Manual Install: -# cd DweebUI -# chmod +x setup.sh -# sudo ./setup.sh - -# Install dependencies -apt-get install -y curl unzip ufw gnupg ca-certificates lsb-release gpg - -# Enable firewall -ufw allow ssh && ufw --force enable - -# Opens port 443/tcp for Reverse Proxy -ufw allow https - -# Install Docker -install -m 0755 -d /etc/apt/keyrings -curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg -chmod a+r /etc/apt/keyrings/docker.gpg - -echo \ - "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \ - "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \ - sudo tee /etc/apt/sources.list.d/docker.list > /dev/null -sudo apt-get update -y -sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin - -# Create docker network -docker network create -d bridge AppBridge - -# Create a redis docker container with persistent storage and a password -docker run -d --name DweebCache --restart unless-stopped -v /home/docker/redis:/data -p 6379:6379 redis redis-server --requirepass "somesupersecretpassword" - - -# Install redis -# curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg -# echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/redis.list -# apt-get update -y -# apt-get install -y redis -# systemctl enable --now redis-server - -# Install nodejs -mkdir -p /etc/apt/keyrings -curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg -NODE_MAJOR=20 -echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list -sudo apt-get update -sudo apt-get install nodejs -y - -npm i - -# Prep for caddy -mkdir -p /home/docker/caddy/sites -echo "import sites/*" > /home/docker/caddy/Caddyfile.tmp -mv /home/docker/caddy/Caddyfile.tmp /home/docker/caddy/Caddyfile - - -# Install pm2 and start DweebUI -npm install pm2 -g -pm2 start app.js --name "dweebui" -pm2 log - - -# Creates a 'docker-compose' alias, since the command changed to 'docker compose' in Debian 11. -echo '#!/bin/sh -docker compose "$@"' > /usr/local/bin/docker-compose -chmod +x /usr/local/bin/docker-compose diff --git a/views/pages/account.ejs b/views/account.ejs similarity index 85% rename from views/pages/account.ejs rename to views/account.ejs index 4274200..2831f17 100644 --- a/views/pages/account.ejs +++ b/views/account.ejs @@ -4,25 +4,25 @@ - Settings - Tabler - Premium and Open Source dashboard template with responsive and high quality UI. + DweebUI - Account - - + + + @import url('fonts/inter.css'); + :root { + --tblr-font-sans-serif: 'Inter Var', -apple-system, BlinkMacSystemFont, San Francisco, Segoe UI, Roboto, Helvetica Neue, sans-serif; + } + body { + font-feature-settings: "cv03", "cv04", "cv11"; + } + - +
- <%- include('../partials/navbar.ejs') %> + <%- include('navbar.ejs') %>
- <%- include('../partials/footer.ejs') %> + <%- include('footer.ejs') %> - - + + \ No newline at end of file diff --git a/views/pages/apps.ejs b/views/apps.ejs similarity index 92% rename from views/pages/apps.ejs rename to views/apps.ejs index 206c2e5..596b34c 100644 --- a/views/pages/apps.ejs +++ b/views/apps.ejs @@ -4,10 +4,10 @@ - Apps list. + DweebUI - Apps - - + + - +
- <%- include('../partials/navbar.ejs') %> + <%- include('navbar.ejs') %>
@@ -99,13 +99,13 @@
- <%- include('../partials/footer.ejs') %> + <%- include('footer.ejs') %> - - + + \ No newline at end of file diff --git a/views/dashboard.ejs b/views/dashboard.ejs new file mode 100644 index 0000000..b76e34a --- /dev/null +++ b/views/dashboard.ejs @@ -0,0 +1,1093 @@ + + + + + + + DweebUI - Dashboard + + + + + + +
+ + <%- include('navbar.ejs') %> +
+ +
+
+
+ +
+
+ +
+
+
+
+
+ + + +
+
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ + + +
+
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ + + +
+
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ + + +
+
+
+ +
+
+ +
+
+
+
+
+
+ + +
+
+ + + + + + + + + + + + + + + + +
+
+
+ + <%- include('footer.ejs') %> + +
+
+ + + + + + + + + + + + + + diff --git a/views/partials/footer.ejs b/views/footer.ejs similarity index 98% rename from views/partials/footer.ejs rename to views/footer.ejs index 8a52e05..cdd7dc2 100644 --- a/views/partials/footer.ejs +++ b/views/footer.ejs @@ -24,7 +24,7 @@
  • - v0.09 + v0.10
  • diff --git a/views/images.ejs b/views/images.ejs new file mode 100644 index 0000000..a893c58 --- /dev/null +++ b/views/images.ejs @@ -0,0 +1,226 @@ + + + + + + + DweebUI - Images + + + + + +
    + + <%- include('navbar.ejs') %> +
    + + + +
    +
    +
    + +
    +
    +
    +

    Docker Images

    + +
    + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Caddy2f148ddb6662b4245ef4ced637e928eaf67a8a1941572d69627652253e779366LatestIn useAugust 05, 202169.27 MBDetails
    clamav/clamavc447f9c713058cdb915c5becbef004a678cd7b8d6f5d70ec53479ba7687c3375LatestIn useJanuary 01, 2019546.22 MBDetails
    linuxserver/code-serveraf09184f86e955b33bef7634862430f54899cc9ca5e492d1530451caa1d99dc0LatestUnusedDecember 28, 201863.91 MBDetails
    +
    + + +
    +
    + +
    +
    +
    + + <%- include('footer.ejs') %> + +
    +
    + + + + + + + + + + + + + \ No newline at end of file diff --git a/views/pages/login.ejs b/views/login.ejs similarity index 54% rename from views/pages/login.ejs rename to views/login.ejs index 4d67cca..b5069ff 100644 --- a/views/pages/login.ejs +++ b/views/login.ejs @@ -6,24 +6,24 @@ DweebUI - Login - - + + - +
    - +
    @@ -36,31 +36,28 @@
    <% } %> -
    +
    - - - - -
    -
    +
    + + Forgot password +
    -
    - Don't have account yet? Sign up +
    + Don't have account? Register
    - - + + \ No newline at end of file diff --git a/views/partials/navbar.ejs b/views/navbar.ejs similarity index 90% rename from views/partials/navbar.ejs rename to views/navbar.ejs index 0438029..9ded793 100644 --- a/views/partials/navbar.ejs +++ b/views/navbar.ejs @@ -1,14 +1,13 @@