diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..ca447caf --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +/src/node_modules \ No newline at end of file diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..7299506c --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,3 @@ +# Copyright (c) Emile Nijssen (WeeJeWel) +# Founder and Codeowner of WireGuard Easy (wg-easy) +* @WeeJeWel diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index dd84ea78..89daa669 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -24,13 +24,13 @@ A clear and concise description of what you expected to happen. If applicable, add screenshots to help explain your problem. **Desktop (please complete the following information):** - - OS: [e.g. iOS] + - OS: [e.g. macOS 12.1] - Browser [e.g. chrome, safari] - Version [e.g. 22] **Smartphone (please complete the following information):** - Device: [e.g. iPhone6] - - OS: [e.g. iOS8.1] + - OS: [e.g. iOS 8.1] - Browser [e.g. stock browser, safari] - Version [e.g. 22] diff --git a/.github/workflows/deploy-pr.yml b/.github/workflows/deploy-pr.yml new file mode 100644 index 00000000..3a3cdb13 --- /dev/null +++ b/.github/workflows/deploy-pr.yml @@ -0,0 +1,38 @@ +name: Build Pull Request + +on: + workflow_dispatch: + pull_request: + +jobs: + deploy: + name: Build & Deploy + runs-on: ubuntu-latest + if: github.repository_owner == 'wg-easy' + permissions: + packages: write + contents: read + steps: + - uses: actions/checkout@v4 + with: + ref: production + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build Docker Image + uses: docker/build-push-action@v5 + with: + push: false + platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8 + tags: ghcr.io/wg-easy/wg-easy:pr diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index cf444d27..26783540 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -17,12 +17,9 @@ jobs: - name: Setup Node uses: actions/setup-node@v4 with: - node-version: '18' + node-version: '20' check-latest: true cache: 'npm' - cache-dependency-path: | - package-lock.json - src/package-lock.json - name: npm run lint run: | diff --git a/.github/workflows/npm-update-bot.yml b/.github/workflows/npm-update-bot.yml index 559da4df..68fc6a35 100644 --- a/.github/workflows/npm-update-bot.yml +++ b/.github/workflows/npm-update-bot.yml @@ -4,7 +4,7 @@ on: push: branches: [ "master" ] schedule: - - cron: "0 0 * * *" + - cron: "0 0 * * 1" jobs: npmupbot: @@ -19,12 +19,9 @@ jobs: - name: Setup Node uses: actions/setup-node@v4 with: - node-version: '18' + node-version: '20' check-latest: true cache: 'npm' - cache-dependency-path: | - package-lock.json - src/package-lock.json - name: Bot 🤖 "Updating NPM Packages..." run: | diff --git a/.gitignore b/.gitignore index 567e6c6f..e6fce2a6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ /config /wg0.conf /wg0.json +/src/node_modules .DS_Store *.swp diff --git a/Dockerfile b/Dockerfile index baf9941b..ca427af2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,16 +1,20 @@ -# There's an issue with node:20-alpine. -# Docker deployment is canceled after 25< minutes. - +# As a workaround we have to build on nodejs 18 +# nodejs 20 hangs on build with armv6/armv7 FROM docker.io/library/node:18-alpine AS build_node_modules +# Update npm to latest +RUN npm install -g npm@latest + # Copy Web UI -COPY src/ /app/ +COPY src /app WORKDIR /app -RUN npm ci --omit=dev +RUN npm ci --omit=dev &&\ + mv node_modules /node_modules # Copy build result to a new image. # This saves a lot of disk space. -FROM docker.io/library/node:18-alpine +FROM docker.io/library/node:20-alpine +HEALTHCHECK CMD /usr/bin/timeout 5s /bin/sh -c "/usr/bin/wg show | /bin/grep -q interface || exit 1" --interval=1m --timeout=5s --retries=3 COPY --from=build_node_modules /app /app # Move node_modules one directory up, so during development @@ -20,13 +24,7 @@ COPY --from=build_node_modules /app /app # Also, some node_modules might be native, and # the architecture & OS of your development machine might differ # than what runs inside of docker. -RUN mv /app/node_modules /node_modules - -# Enable this to run `npm run serve` -RUN npm i -g nodemon - -# Workaround CVE-2023-42282 -RUN npm uninstall -g ip +COPY --from=build_node_modules /node_modules /node_modules # Install Linux packages RUN apk add --no-cache \ @@ -39,10 +37,6 @@ RUN apk add --no-cache \ # Use iptables-legacy RUN update-alternatives --install /sbin/iptables iptables /sbin/iptables-legacy 10 --slave /sbin/iptables-restore iptables-restore /sbin/iptables-legacy-restore --slave /sbin/iptables-save iptables-save /sbin/iptables-legacy-save -# Expose Ports -EXPOSE 51820/udp -EXPOSE 51821/tcp - # Set Environment ENV DEBUG=Server,WireGuard diff --git a/README.md b/README.md index 61eda10f..47a7b3d6 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,6 @@ You have found the easiest way to install & manage WireGuard on any Linux host!
## Features - * All-in-one: WireGuard + Web UI. * Easy installation, simple to use. * List, create, edit, delete, enable & disable clients. @@ -23,12 +22,25 @@ You have found the easiest way to install & manage WireGuard on any Linux host! * Tx/Rx charts for each connected client. * Gravatar support. * Automatic Light / Dark Mode +* Multilanguage Support +* UI_TRAFFIC_STATS (default off) ## Requirements * A host with a kernel that supports WireGuard (all modern kernels). * A host with Docker installed. +## Versions + +We provide more then 1 docker image to get, this will help you decide which one is best for you. + +| tag | Branch | Example | Description | +| - | - | - | - | +| `latest` | production | `ghcr.io/wg-easy/wg-easy:latest` or `ghcr.io/wg-easy/wg-easy` | stable as possbile get bug fixes quickly when needed, deployed against `production`. | +| `13` | production | `ghcr.io/wg-easy/wg-easy:13` | same as latest, stick to a version tag. | +| `nightly` | master | `ghcr.io/wg-easy/wg-easy:nightly` | mostly unstable gets frequent package and code updates, deployed against `master`. | +| `development` | pull requests | `ghcr.io/wg-easy/wg-easy:development` | used for development, testing code from PRs before landing into `master`. | + ## Installation ### 1. Install Docker @@ -36,9 +48,9 @@ You have found the easiest way to install & manage WireGuard on any Linux host! If you haven't installed Docker yet, install it by running: ```bash -$ curl -sSL https://get.docker.com | sh -$ sudo usermod -aG docker $(whoami) -$ exit +curl -sSL https://get.docker.com | sh +sudo usermod -aG docker $(whoami) +exit ``` And log in again. @@ -47,12 +59,14 @@ And log in again. To automatically install & run wg-easy, simply run: --$ docker run -d \ +``` + docker run -d \ --name=wg-easy \ -e LANG=de \ - -e WG_HOST=🚨YOUR_SERVER_IP \ - -e PASSWORD=🚨YOUR_ADMIN_PASSWORD \ + -e WG_HOST=<🚨YOUR_SERVER_IP> \ + -e PASSWORD_HASH=<🚨YOUR_ADMIN_PASSWORD_HASH> \ + -e PORT=51821 \ + -e WG_PORT=51820 \ -v ~/.wg-easy:/etc/wireguard \ -p 51820:51820/udp \ -p 51821:51821/tcp \ @@ -62,16 +76,20 @@ $ docker run -d \ --sysctl="net.ipv4.ip_forward=1" \ --restart unless-stopped \ ghcr.io/wg-easy/wg-easy -+``` > 💡 Replace `YOUR_SERVER_IP` with your WAN IP, or a Dynamic DNS hostname. > -> 💡 Replace `YOUR_ADMIN_PASSWORD` with a password to log in on the Web UI. +> 💡 Replace `YOUR_ADMIN_PASSWORD_HASH` with a bcrypt password hash to log in on the Web UI. The Web UI will now be available on `http://0.0.0.0:51821`. > 💡 Your configuration files will be saved in `~/.wg-easy` +WireGuard Easy can be launched with Docker Compose as well - just download +[`docker-compose.yml`](docker-compose.yml), make necessary adjustments and +execute `docker compose up --detach`. + ### 3. Sponsor Are you enjoying this project? [Buy Emile a beer!](https://github.com/sponsors/WeeJeWel) 🍻 @@ -84,10 +102,12 @@ These options can be configured by setting environment variables using `-e KEY=" | - | - | - | - | | `PORT` | `51821` | `6789` | TCP port for Web UI. | | `WEBUI_HOST` | `0.0.0.0` | `localhost` | IP address web UI binds to. | -| `PASSWORD` | - | `foobar123` | When set, requires a password when logging in to the Web UI. | +| `PASSWORD_HASH` | - | `$2y$05$Ci...` | When set, requires a password when logging in to the Web UI. | +| `PASSWORD` (deprecated) | - | `foobar123` | When set, requires a password when logging in to the Web UI. *(Not used if `PASSWORD_HASH` is set)* | | `WG_HOST` | - | `vpn.myserver.com` | The public hostname of your VPN server. | | `WG_DEVICE` | `eth0` | `ens6f0` | Ethernet device the wireguard traffic should be forwarded through. | -| `WG_PORT` | `51820` | `12345` | The public UDP port of your VPN server. WireGuard will always listen on 51820 inside the Docker container. | +| `WG_PORT` | `51820` | `12345` | The public UDP port of your VPN server. WireGuard will listen on that (othwise default) inside the Docker container. | +| `WG_CONFIG_PORT`| `51820` | `12345` | The UDP port used on [Home Assistent Plugin](https://github.com/adriy-be/homeassistant-addons-jdeath/tree/main/wgeasy) | `WG_MTU` | `null` | `1420` | The MTU the clients will use. Server uses default WG MTU. | | `WG_PERSISTENT_KEEPALIVE` | `0` | `25` | Value in seconds to keep the "connection" open. If this value is 0, then connections won't be kept alive. | | `WG_DEFAULT_ADDRESS` | `10.8.0.x` | `10.6.0.x` | Clients IP address range. | @@ -97,7 +117,9 @@ These options can be configured by setting environment variables using `-e KEY=" | `WG_POST_UP` | `...` | `iptables ...` | See [config.js](https://github.com/wg-easy/wg-easy/blob/master/src/config.js#L20) for the default value. | | `WG_PRE_DOWN` | `...` | - | See [config.js](https://github.com/wg-easy/wg-easy/blob/master/src/config.js#L27) for the default value. | | `WG_POST_DOWN` | `...` | `iptables ...` | See [config.js](https://github.com/wg-easy/wg-easy/blob/master/src/config.js#L28) for the default value. | -| `LANG` | `en` | `de` | Web UI language (Supports: en, ru, tr, no, pl, fr, de, ca, es, vi, nl, is, chs, cht,). | +| `LANG` | `en` | `de` | Web UI language (Supports: en, ua, ru, tr, no, pl, fr, de, ca, es, ko, vi, nl, is, pt, chs, cht, it, th, hi). | +| `UI_TRAFFIC_STATS` | `false` | `true` | Enable detailed RX / TX client stats in Web UI | +| `UI_CHART_TYPE` | `0` | `1` | UI_CHART_TYPE=0 # Charts disabled, UI_CHART_TYPE=1 # Line chart, UI_CHART_TYPE=2 # Area chart, UI_CHART_TYPE=3 # Bar chart | > If you change `WG_PORT`, make sure to also change the exposed port. @@ -113,6 +135,14 @@ docker pull ghcr.io/wg-easy/wg-easy And then run the `docker run -d \ ...` command above again. +With Docker Compose WireGuard Easy can be updated with a single command: +`docker compose up --detach --pull always` (if an image tag is specified in the +Compose file and it is not `latest`, make sure that it is changed to the desired +one; by default it is omitted and +[defaults to `latest`](https://docs.docker.com/engine/reference/run/#image-references)). \ +The WireGuared Easy container will be automatically recreated if a newer image +was pulled. + ## Common Use Cases * [Using WireGuard-Easy with Pi-Hole](https://github.com/wg-easy/wg-easy/wiki/Using-WireGuard-Easy-with-Pi-Hole) diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 08e46978..65e64297 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -1,4 +1,3 @@ -version: "3.8" services: wg-easy: image: wg-easy diff --git a/docker-compose.yml b/docker-compose.yml index 2c529a68..daba03f6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,3 @@ -version: "3.8" volumes: etc_wireguard: @@ -6,7 +5,7 @@ services: wg-easy: environment: # Change Language: - # (Supports: en, ua, ru, tr, no, pl, fr, de, ca, es, pt, chs, cht) + # (Supports: en, ua, ru, tr, no, pl, fr, de, ca, es, ko, vi, nl, is, pt, chs, cht, it, th, hi) - LANG=de # ⚠️ Required: # Change this to your host's public address @@ -14,7 +13,9 @@ services: # Optional: # - PASSWORD=foobar123 + # - PORT=51821 # - WG_PORT=51820 + # - WG_CONFIG_PORT=92820 # - WG_DEFAULT_ADDRESS=10.8.0.x # - WG_DEFAULT_DNS=1.1.1.1 # - WG_MTU=1420 @@ -24,6 +25,8 @@ services: # - WG_POST_UP=echo "Post Up" > /etc/wireguard/post-up.txt # - WG_PRE_DOWN=echo "Pre Down" > /etc/wireguard/pre-down.txt # - WG_POST_DOWN=echo "Post Down" > /etc/wireguard/post-down.txt + # - UI_TRAFFIC_STATS=true + # - UI_CHART_TYPE=0 # (0 Charts disabled, 1 # Line chart, 2 # Area chart, 3 # Bar chart) image: ghcr.io/wg-easy/wg-easy container_name: wg-easy @@ -36,6 +39,7 @@ services: cap_add: - NET_ADMIN - SYS_MODULE + # - NET_RAW # ⚠️ Uncomment if using Podman sysctls: - net.ipv4.ip_forward=1 - net.ipv4.conf.all.src_valid_mark=1 diff --git a/docs/changelog.json b/docs/changelog.json index fe4431f4..1f16d864 100644 --- a/docs/changelog.json +++ b/docs/changelog.json @@ -9,5 +9,7 @@ "8": "Updated to Node.js v18.", "9": "Fixed issue running on devices with older kernels.", "10": "Added sessionless HTTP API auth & automatic dark mode.", - "11": "Multilanguage Support & various bugfixes" + "11": "Multilanguage Support & various bugfixes.", + "12": "UI_TRAFFIC_STATS, Import json configurations with no PreShared-Key, allow clients with no privateKey & more.", + "13": "New framework (h3), UI_CHART_TYPE, some bugfixes and more." } diff --git a/package.json b/package.json index ded9b4e9..c0485821 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "version": "1.0.1", "scripts": { "build": "DOCKER_BUILDKIT=1 docker build --tag wg-easy .", - "serve": "docker-compose -f docker-compose.yml -f docker-compose.dev.yml up", + "serve": "docker compose -f docker-compose.yml -f docker-compose.dev.yml up", "start": "docker run --env WG_HOST=0.0.0.0 --name wg-easy --cap-add=NET_ADMIN --cap-add=SYS_MODULE --sysctl=\"net.ipv4.conf.all.src_valid_mark=1\" --mount type=bind,source=\"$(pwd)\"/config,target=/etc/wireguard -p 51820:51820/udp -p 51821:51821/tcp wg-easy" } } diff --git a/src/.gitignore b/src/.gitignore deleted file mode 100644 index 07e6e472..00000000 --- a/src/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/node_modules diff --git a/src/config.js b/src/config.js index e8e38718..773a0c5c 100644 --- a/src/config.js +++ b/src/config.js @@ -3,15 +3,17 @@ const { release } = require('./package.json'); module.exports.RELEASE = release; -module.exports.PORT = process.env.PORT || 51821; +module.exports.PORT = process.env.PORT || '51821'; module.exports.WEBUI_HOST = process.env.WEBUI_HOST || '0.0.0.0'; module.exports.PASSWORD = process.env.PASSWORD; +module.exports.PASSWORD_HASH = process.env.PASSWORD_HASH; module.exports.WG_PATH = process.env.WG_PATH || '/etc/wireguard/'; module.exports.WG_DEVICE = process.env.WG_DEVICE || 'eth0'; module.exports.WG_HOST = process.env.WG_HOST; -module.exports.WG_PORT = process.env.WG_PORT || 51820; +module.exports.WG_PORT = process.env.WG_PORT || '51820'; +module.exports.WG_CONFIG_PORT = process.env.WG_CONFIG_PORT || process.env.WG_PORT || '51820'; module.exports.WG_MTU = process.env.WG_MTU || null; -module.exports.WG_PERSISTENT_KEEPALIVE = process.env.WG_PERSISTENT_KEEPALIVE || 0; +module.exports.WG_PERSISTENT_KEEPALIVE = process.env.WG_PERSISTENT_KEEPALIVE || '0'; module.exports.WG_DEFAULT_ADDRESS = process.env.WG_DEFAULT_ADDRESS || '10.8.0.x'; module.exports.WG_DEFAULT_DNS = typeof process.env.WG_DEFAULT_DNS === 'string' ? process.env.WG_DEFAULT_DNS @@ -22,11 +24,18 @@ module.exports.WG_HOMESERVER_ALLOWED_IPS = process.env.WG_HOMESERVER_ALLOWED_IPS module.exports.WG_PRE_UP = process.env.WG_PRE_UP || ''; module.exports.WG_POST_UP = process.env.WG_POST_UP || ` iptables -t nat -A POSTROUTING -s ${module.exports.WG_DEFAULT_ADDRESS.replace('x', '0')}/24 -o ${module.exports.WG_DEVICE} -j MASQUERADE; -iptables -A INPUT -p udp -m udp --dport 51820 -j ACCEPT; +iptables -A INPUT -p udp -m udp --dport ${module.exports.WG_PORT} -j ACCEPT; iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; `.split('\n').join(' '); module.exports.WG_PRE_DOWN = process.env.WG_PRE_DOWN || ''; -module.exports.WG_POST_DOWN = process.env.WG_POST_DOWN || ''; +module.exports.WG_POST_DOWN = process.env.WG_POST_DOWN || ` +iptables -t nat -D POSTROUTING -s ${module.exports.WG_DEFAULT_ADDRESS.replace('x', '0')}/24 -o ${module.exports.WG_DEVICE} -j MASQUERADE; +iptables -D INPUT -p udp -m udp --dport ${module.exports.WG_PORT} -j ACCEPT; +iptables -D FORWARD -i wg0 -j ACCEPT; +iptables -D FORWARD -o wg0 -j ACCEPT; +`.split('\n').join(' '); module.exports.LANG = process.env.LANG || 'en'; +module.exports.UI_TRAFFIC_STATS = process.env.UI_TRAFFIC_STATS || 'false'; +module.exports.UI_CHART_TYPE = process.env.UI_CHART_TYPE || 0; diff --git a/src/lib/Server.js b/src/lib/Server.js index 1f00a6b4..40341ee5 100644 --- a/src/lib/Server.js +++ b/src/lib/Server.js @@ -1,15 +1,27 @@ 'use strict'; -const path = require('path'); const bcrypt = require('bcryptjs'); const crypto = require('node:crypto'); +const { createServer } = require('node:http'); +const { stat, readFile } = require('node:fs/promises'); +const { resolve, sep } = require('node:path'); -const express = require('express'); const expressSession = require('express-session'); const debug = require('debug')('Server'); -const Util = require('./Util'); -const ServerError = require('./ServerError'); +const { + createApp, + createError, + createRouter, + defineEventHandler, + fromNodeMiddleware, + getRouterParam, + toNodeListener, + readBody, + setHeader, + serveStatic, +} = require('h3'); + const WireGuard = require('../services/WireGuard'); const { @@ -17,39 +29,80 @@ const { WEBUI_HOST, RELEASE, PASSWORD, + PASSWORD_HASH, LANG, + UI_TRAFFIC_STATS, + UI_CHART_TYPE, } = require('../config'); +const requiresPassword = !!PASSWORD || !!PASSWORD_HASH; + +/** + * Checks if `password` matches the PASSWORD_HASH. + * + * For backward compatibility it also allows `password` to match the clear text PASSWORD, + * but only if no PASSWORD_HASH is provided. + * + * If both enviornment variables are not set, the password is always invalid. + * + * @param {string} password String to test + * @returns {boolean} true if matching environment, otherwise false + */ +const isPasswordValid = (password) => { + if (typeof password !== 'string') { + return false; + } + + if (PASSWORD_HASH) { + return bcrypt.compareSync(password, PASSWORD_HASH); + } + if (PASSWORD) { + return password === PASSWORD; + } + + return false; +}; + module.exports = class Server { constructor() { - // Express - this.app = express() - .disable('etag') - .use('/', express.static(path.join(__dirname, '..', 'www'))) - .use(express.json()) - .use(expressSession({ - secret: crypto.randomBytes(256).toString('hex'), - resave: true, - saveUninitialized: true, - cookie: { - httpOnly: true, - }, - })) + const app = createApp(); + this.app = app; + + app.use(fromNodeMiddleware(expressSession({ + secret: crypto.randomBytes(256).toString('hex'), + resave: true, + saveUninitialized: true, + }))); - .get('/api/release', (Util.promisify(async () => { + const router = createRouter(); + app.use(router); + + router + .get('/api/release', defineEventHandler((event) => { + setHeader(event, 'Content-Type', 'application/json'); return RELEASE; - }))) + })) + + .get('/api/lang', defineEventHandler((event) => { + setHeader(event, 'Content-Type', 'application/json'); + return `"${LANG}"`; + })) + + .get('/api/ui-traffic-stats', defineEventHandler((event) => { + setHeader(event, 'Content-Type', 'application/json'); + return `"${UI_TRAFFIC_STATS}"`; + })) - .get('/api/lang', (Util.promisify(async () => { - return LANG; - }))) + .get('/api/ui-chart-type', defineEventHandler((event) => { + setHeader(event, 'Content-Type', 'application/json'); + return `"${UI_CHART_TYPE}"`; + })) - // Authentication - .get('/api/session', Util.promisify(async (req) => { - const requiresPassword = !!process.env.PASSWORD; + // Authentication + .get('/api/session', defineEventHandler((event) => { const authenticated = requiresPassword - ? !!(req.session && req.session.authenticated) + ? !!(event.node.req.session && event.node.req.session.authenticated) : true; return { @@ -57,28 +110,37 @@ module.exports = class Server { authenticated, }; })) - .post('/api/session', Util.promisify(async (req) => { - const { - password, - } = req.body; + .post('/api/session', defineEventHandler(async (event) => { + const { password } = await readBody(event); - if (typeof password !== 'string') { - throw new ServerError('Missing: Password', 401); + if (!requiresPassword) { + // if no password is required, the API should never be called. + // Do not automatically authenticate the user. + throw createError({ + status: 401, + message: 'Invalid state', + }); } - if (password !== PASSWORD) { - throw new ServerError('Incorrect Password', 401); + if (!isPasswordValid(password)) { + throw createError({ + status: 401, + message: 'Incorrect Password', + }); } - req.session.authenticated = true; - req.session.save(); + event.node.req.session.authenticated = true; + event.node.req.session.save(); - debug(`New Session: ${req.session.id}`); - })) + debug(`New Session: ${event.node.req.session.id}`); + + return { success: true }; + })); // WireGuard - .use((req, res, next) => { - if (!PASSWORD) { + app.use( + fromNodeMiddleware((req, res, next) => { + if (!requiresPassword || !req.url.startsWith('/api/')) { return next(); } @@ -86,8 +148,8 @@ module.exports = class Server { return next(); } - if (req.path.startsWith('/api/') && req.headers['authorization']) { - if (bcrypt.compareSync(req.headers['authorization'], bcrypt.hashSync(PASSWORD, 10))) { + if (req.url.startsWith('/api/') && req.headers['authorization']) { + if (isPasswordValid(req.headers['authorization'])) { return next(); } return res.status(401).json({ @@ -98,25 +160,32 @@ module.exports = class Server { return res.status(401).json({ error: 'Not Logged In', }); - }) - .delete('/api/session', Util.promisify(async (req) => { - const sessionId = req.session.id; + }), + ); - req.session.destroy(); + const router2 = createRouter(); + app.use(router2); + + router2 + .delete('/api/session', defineEventHandler((event) => { + const sessionId = event.node.req.session.id; + + event.node.req.session.destroy(); debug(`Deleted Session: ${sessionId}`); + return { success: true }; })) - .get('/api/wireguard/client', Util.promisify(async (req) => { + .get('/api/wireguard/client', defineEventHandler(() => { return WireGuard.getClients(); })) - .get('/api/wireguard/client/:clientId/qrcode.svg', Util.promisify(async (req, res) => { - const { clientId } = req.params; + .get('/api/wireguard/client/:clientId/qrcode.svg', defineEventHandler(async (event) => { + const clientId = getRouterParam(event, 'clientId'); const svg = await WireGuard.getClientQRCodeSVG({ clientId }); - res.header('Content-Type', 'image/svg+xml'); - res.send(svg); + setHeader(event, 'Content-Type', 'image/svg+xml'); + return svg; })) - .get('/api/wireguard/client/:clientId/configuration', Util.promisify(async (req, res) => { - const { clientId } = req.params; + .get('/api/wireguard/client/:clientId/configuration', defineEventHandler(async (event) => { + const clientId = getRouterParam(event, 'clientId'); const client = await WireGuard.getClient({ clientId }); const config = await WireGuard.getClientConfiguration({ clientId }); const configName = client.name @@ -124,52 +193,111 @@ module.exports = class Server { .replace(/(-{2,}|-$)/g, '-') .replace(/-$/, '') .substring(0, 32); - res.header('Content-Disposition', `attachment; filename="${configName || clientId}.conf"`); - res.header('Content-Type', 'text/plain'); - res.send(config); + setHeader(event, 'Content-Disposition', `attachment; filename="${configName || clientId}.conf"`); + setHeader(event, 'Content-Type', 'text/plain'); + return config; })) - .post('/api/wireguard/client', Util.promisify(async (req) => { - const { name } = req.body; - return WireGuard.createClient({ name }); + .post('/api/wireguard/client', defineEventHandler(async (event) => { + const { name } = await readBody(event); + await WireGuard.createClient({ name }); + return { success: true }; })) - .delete('/api/wireguard/client/:clientId', Util.promisify(async (req) => { - const { clientId } = req.params; - return WireGuard.deleteClient({ clientId }); + .delete('/api/wireguard/client/:clientId', defineEventHandler(async (event) => { + const clientId = getRouterParam(event, 'clientId'); + await WireGuard.deleteClient({ clientId }); + return { success: true }; })) - .post('/api/wireguard/client/:clientId/enable', Util.promisify(async (req, res) => { - const { clientId } = req.params; + .post('/api/wireguard/client/:clientId/enable', defineEventHandler(async (event) => { + const clientId = getRouterParam(event, 'clientId'); if (clientId === '__proto__' || clientId === 'constructor' || clientId === 'prototype') { - res.end(403); + throw createError({ status: 403 }); } - return WireGuard.enableClient({ clientId }); + await WireGuard.enableClient({ clientId }); + return { success: true }; })) - .post('/api/wireguard/client/:clientId/disable', Util.promisify(async (req, res) => { - const { clientId } = req.params; + .post('/api/wireguard/client/:clientId/disable', defineEventHandler(async (event) => { + const clientId = getRouterParam(event, 'clientId'); if (clientId === '__proto__' || clientId === 'constructor' || clientId === 'prototype') { - res.end(403); + throw createError({ status: 403 }); } - return WireGuard.disableClient({ clientId }); + await WireGuard.disableClient({ clientId }); + return { success: true }; })) - .put('/api/wireguard/client/:clientId/name', Util.promisify(async (req, res) => { - const { clientId } = req.params; + .put('/api/wireguard/client/:clientId/name', defineEventHandler(async (event) => { + const clientId = getRouterParam(event, 'clientId'); if (clientId === '__proto__' || clientId === 'constructor' || clientId === 'prototype') { - res.end(403); + throw createError({ status: 403 }); } - const { name } = req.body; - return WireGuard.updateClientName({ clientId, name }); + const { name } = await readBody(event); + await WireGuard.updateClientName({ clientId, name }); + return { success: true }; })) - .put('/api/wireguard/client/:clientId/address', Util.promisify(async (req, res) => { - const { clientId } = req.params; + .put('/api/wireguard/client/:clientId/address', defineEventHandler(async (event) => { + const clientId = getRouterParam(event, 'clientId'); if (clientId === '__proto__' || clientId === 'constructor' || clientId === 'prototype') { - res.end(403); + throw createError({ status: 403 }); } - const { address } = req.body; - return WireGuard.updateClientAddress({ clientId, address }); - })) + const { address } = await readBody(event); + await WireGuard.updateClientAddress({ clientId, address }); + return { success: true }; + })); + + const safePathJoin = (base, target) => { + // Manage web root (edge case) + if (target === '/') { + return `${base}${sep}`; + } - .listen(PORT, WEBUI_HOST, () => { - debug(`Listening on http://${WEBUI_HOST}:${PORT}`); + // Prepend './' to prevent absolute paths + const targetPath = `.${sep}${target}`; + + // Resolve the absolute path + const resolvedPath = resolve(base, targetPath); + + // Check if resolvedPath is a subpath of base + if (resolvedPath.startsWith(`${base}${sep}`)) { + return resolvedPath; + } + + throw createError({ + status: 400, + message: 'Bad Request', }); + }; + + // Static assets + const publicDir = '/app/www'; + app.use( + defineEventHandler((event) => { + return serveStatic(event, { + getContents: (id) => { + return readFile(safePathJoin(publicDir, id)); + }, + getMeta: async (id) => { + const filePath = safePathJoin(publicDir, id); + + const stats = await stat(filePath).catch(() => {}); + if (!stats || !stats.isFile()) { + return; + } + + if (id.endsWith('.html')) setHeader(event, 'Content-Type', 'text/html'); + if (id.endsWith('.js')) setHeader(event, 'Content-Type', 'application/javascript'); + if (id.endsWith('.json')) setHeader(event, 'Content-Type', 'application/json'); + if (id.endsWith('.css')) setHeader(event, 'Content-Type', 'text/css'); + if (id.endsWith('.png')) setHeader(event, 'Content-Type', 'image/png'); + + return { + size: stats.size, + mtime: stats.mtimeMs, + }; + }, + }); + }), + ); + + createServer(toNodeListener(app)).listen(PORT, WEBUI_HOST); + debug(`Listening on http://${WEBUI_HOST}:${PORT}`); } }; diff --git a/src/lib/WireGuard.js b/src/lib/WireGuard.js index 417a7cfd..006dd1c7 100644 --- a/src/lib/WireGuard.js +++ b/src/lib/WireGuard.js @@ -1,10 +1,9 @@ 'use strict'; -const fs = require('fs').promises; +const fs = require('node:fs/promises'); const path = require('path'); - const debug = require('debug')('WireGuard'); -const uuid = require('uuid'); +const crypto = require('node:crypto'); const QRCode = require('qrcode'); const Util = require('./Util'); @@ -14,6 +13,7 @@ const { WG_PATH, WG_HOST, WG_PORT, + WG_CONFIG_PORT, WG_MTU, WG_DEFAULT_DNS, WG_DEFAULT_ADDRESS, @@ -96,7 +96,7 @@ module.exports = class WireGuard { [Interface] PrivateKey = ${config.server.privateKey} Address = ${config.server.address}/24 -ListenPort = 51820 +ListenPort = ${WG_PORT} PreUp = ${WG_PRE_UP} PostUp = ${WG_POST_UP} PreDown = ${WG_PRE_DOWN} @@ -111,8 +111,8 @@ PostDown = ${WG_POST_DOWN} # Client: ${client.name} (${clientId}) [Peer] PublicKey = ${client.publicKey} -PresharedKey = ${client.preSharedKey} -AllowedIPs = ${client.address}/32`; +${client.preSharedKey ? `PresharedKey = ${client.preSharedKey}\n` : '' +}AllowedIPs = ${client.address}/32`; debug(client.name); debug(client.name === 'Homeserver'); if (client.name === 'Homeserver' && WG_HOMESERVER_ALLOWED_IPS) { @@ -147,7 +147,7 @@ AllowedIPs = ${client.address}/32`; createdAt: new Date(client.createdAt), updatedAt: new Date(client.updatedAt), allowedIPs: client.allowedIPs, - + downloadableConfig: 'privateKey' in client, persistentKeepalive: null, latestHandshakeAt: null, transferRx: null, @@ -202,18 +202,19 @@ AllowedIPs = ${client.address}/32`; const config = await this.getConfig(); const client = await this.getClient({ clientId }); - return `[Interface] -PrivateKey = ${client.privateKey} + return ` +[Interface] +PrivateKey = ${client.privateKey ? `${client.privateKey}` : 'REPLACE_ME'} Address = ${client.address}/24 ${WG_DEFAULT_DNS ? `DNS = ${WG_DEFAULT_DNS}\n` : ''}\ ${WG_MTU ? `MTU = ${WG_MTU}\n` : ''}\ [Peer] PublicKey = ${config.server.publicKey} -PresharedKey = ${client.preSharedKey} -AllowedIPs = ${WG_ALLOWED_IPS} +${client.preSharedKey ? `PresharedKey = ${client.preSharedKey}\n` : '' +}AllowedIPs = ${WG_ALLOWED_IPS} PersistentKeepalive = ${WG_PERSISTENT_KEEPALIVE} -Endpoint = ${WG_HOST}:${WG_PORT}`; +Endpoint = ${WG_HOST}:${WG_CONFIG_PORT}`; } async getClientQRCodeSVG({ clientId }) { @@ -253,7 +254,7 @@ Endpoint = ${WG_HOST}:${WG_PORT}`; } // Create Client - const id = uuid.v4(); + const id = crypto.randomUUID(); const client = { id, name, @@ -324,4 +325,9 @@ Endpoint = ${WG_HOST}:${WG_PORT}`; await this.saveConfig(); } + // Shutdown wireguard + async Shutdown() { + await Util.exec('wg-quick down wg0').catch(() => { }); + } + }; diff --git a/src/package-lock.json b/src/package-lock.json index c9d9939f..fdd1d3fd 100644 --- a/src/package-lock.json +++ b/src/package-lock.json @@ -7,21 +7,21 @@ "": { "name": "wg-easy", "version": "1.0.1", - "license": "GPL", + "license": "CC BY-NC-SA 4.0", "dependencies": { "bcryptjs": "^2.4.3", "debug": "^4.3.5", - "express": "^4.19.2", "express-session": "^1.18.0", - "qrcode": "^1.5.3", - "uuid": "^10.0.0" + "h3": "^1.11.1", + "qrcode": "^1.5.3" }, "devDependencies": { "eslint-config-athom": "^3.1.3", + "nodemon": "^3.1.3", "tailwindcss": "^3.4.4" }, "engines": { - "node": "18" + "node": ">=18" } }, "node_modules/@alloc/quick-lru": { @@ -195,17 +195,6 @@ "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/@eslint/eslintrc/node_modules/ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", @@ -216,19 +205,6 @@ "node": ">= 4" } }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "peer": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/@humanwhocodes/config-array": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", @@ -245,30 +221,6 @@ "node": ">=10.10.0" } }, - "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "peer": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/@humanwhocodes/object-schema": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", @@ -632,6 +584,30 @@ } } }, + "node_modules/@typescript-eslint/typescript-estree/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==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@typescript-eslint/utils": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", @@ -674,18 +650,6 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/acorn": { "version": "7.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", @@ -809,11 +773,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, "node_modules/array-includes": { "version": "3.1.8", "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", @@ -969,49 +928,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, "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==", + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, "node_modules/braces": { @@ -1026,18 +950,11 @@ "node": ">=8" } }, - "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/call-bind": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dev": true, "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -1179,23 +1096,12 @@ "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", "dev": true }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dependencies": { - "safe-buffer": "5.2.1" - }, + "node_modules/consola": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.2.3.tgz", + "integrity": "sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==", "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "engines": { - "node": ">= 0.6" + "node": "^14.18.0 || >=16.10.0" } }, "node_modules/cookie": { @@ -1206,10 +1112,15 @@ "node": ">= 0.6" } }, + "node_modules/cookie-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.1.0.tgz", + "integrity": "sha512-L2rLOcK0wzWSfSDA33YR+PUHDG10a8px7rUHKWbGLP4YfbsMed2KFUw5fczvDPbT98DDe3LEzviswl810apTEw==" + }, "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==" }, "node_modules/cross-spawn": { "version": "7.0.3", @@ -1225,6 +1136,19 @@ "node": ">= 8" } }, + "node_modules/crossws": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/crossws/-/crossws-0.2.4.tgz", + "integrity": "sha512-DAxroI2uSOgUKLz00NX6A8U/8EE3SZHmIND+10jkVSaypvyt57J5JEOxAQOL6lQxyzi/wZbTIwssU1uy69h5Vg==", + "peerDependencies": { + "uWebSockets.js": "*" + }, + "peerDependenciesMeta": { + "uWebSockets.js": { + "optional": true + } + } + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -1323,6 +1247,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -1352,6 +1277,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/defu": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", + "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==" + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -1360,14 +1290,10 @@ "node": ">= 0.8" } }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } + "node_modules/destr": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.3.tgz", + "integrity": "sha512-2N3BOUU4gYMpTP24s5rF5iP7BDr7uNTCs4ozw3kf/eKfvWSIu93GEBi5m427YoyJoeOzQ5smuu4nNAPGb8idSQ==" }, "node_modules/didyoumean": { "version": "1.2.2", @@ -1417,11 +1343,6 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -1432,14 +1353,6 @@ "resolved": "https://registry.npmjs.org/encode-utf8/-/encode-utf8-1.0.3.tgz", "integrity": "sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==" }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/enquirer": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", @@ -1518,6 +1431,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, "dependencies": { "get-intrinsic": "^1.2.4" }, @@ -1529,6 +1443,19 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0" + }, "engines": { "node": ">= 0.4" } @@ -1585,11 +1512,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "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", @@ -1809,16 +1731,6 @@ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" } }, - "node_modules/eslint-plugin-import/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/eslint-plugin-import/node_modules/debug": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", @@ -1840,18 +1752,6 @@ "node": ">=0.10.0" } }, - "node_modules/eslint-plugin-import/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/eslint-plugin-import/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -1897,28 +1797,6 @@ "eslint": ">=5.16.0" } }, - "node_modules/eslint-plugin-node/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint-plugin-node/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/eslint-plugin-node/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -1978,17 +1856,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/eslint/node_modules/eslint-visitor-keys": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", @@ -2022,19 +1889,6 @@ "node": ">= 4" } }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "peer": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/espree": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", @@ -2139,55 +1993,6 @@ "node": ">=0.10.0" } }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express": { - "version": "4.19.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", - "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.2", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.6.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, "node_modules/express-session": { "version": "1.18.0", "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.0.tgz", @@ -2206,11 +2011,6 @@ "node": ">= 0.8.0" } }, - "node_modules/express-session/node_modules/cookie-signature": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", - "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==" - }, "node_modules/express-session/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -2224,19 +2024,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -2320,36 +2107,6 @@ "node": ">=8" } }, - "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, "node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -2403,26 +2160,10 @@ "signal-exit": "^4.0.1" }, "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "engines": { - "node": ">= 0.6" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/fs.realpath": { @@ -2450,6 +2191,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -2500,6 +2242,7 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dev": true, "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2", @@ -2565,30 +2308,6 @@ "node": ">=10.13.0" } }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "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==", - "dev": true, - "peer": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/globals": { "version": "13.24.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", @@ -2645,6 +2364,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, "dependencies": { "get-intrinsic": "^1.1.3" }, @@ -2658,6 +2378,23 @@ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, + "node_modules/h3": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/h3/-/h3-1.11.1.tgz", + "integrity": "sha512-AbaH6IDnZN6nmbnJOH72y3c5Wwh9P97soSVdGSBbcDACRdkC0FEWf25pzx4f/NuOCK6quHmW18yF2Wx+G4Zi1A==", + "dependencies": { + "cookie-es": "^1.0.0", + "crossws": "^0.2.2", + "defu": "^6.1.4", + "destr": "^2.0.3", + "iron-webcrypto": "^1.0.0", + "ohash": "^1.1.3", + "radix3": "^1.1.0", + "ufo": "^1.4.0", + "uncrypto": "^0.1.3", + "unenv": "^1.9.0" + } + }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -2681,6 +2418,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, "dependencies": { "es-define-property": "^1.0.0" }, @@ -2692,6 +2430,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true, "engines": { "node": ">= 0.4" }, @@ -2703,6 +2442,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, "engines": { "node": ">= 0.4" }, @@ -2729,6 +2469,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, "dependencies": { "function-bind": "^1.1.2" }, @@ -2736,32 +2477,6 @@ "node": ">= 0.4" } }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", @@ -2771,6 +2486,12 @@ "node": ">= 4" } }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -2813,7 +2534,9 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "peer": true }, "node_modules/internal-slot": { "version": "1.0.7", @@ -2829,12 +2552,12 @@ "node": ">= 0.4" } }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "engines": { - "node": ">= 0.10" + "node_modules/iron-webcrypto": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz", + "integrity": "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==", + "funding": { + "url": "https://github.com/sponsors/brc-dd" } }, "node_modules/is-array-buffer": { @@ -3266,19 +2989,6 @@ "node": "14 || >=16.14" } }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" - }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -3288,14 +2998,6 @@ "node": ">= 8" } }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/micromatch": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", @@ -3310,48 +3012,26 @@ } }, "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", "bin": { "mime": "cli.js" }, "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" + "node": ">=10.0.0" } }, "node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "*" } }, "node_modules/minimist": { @@ -3412,12 +3092,58 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "node_modules/node-fetch-native": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.4.tgz", + "integrity": "sha512-IhOigYzAKHd244OC0JIMIUrjzctirCmPkaIfhDeGcEETWof5zKYUW7e7MYvChGWh/4CJeXEgsRyGzuF334rOOQ==" + }, + "node_modules/nodemon": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.3.tgz", + "integrity": "sha512-m4Vqs+APdKzDFpuaL9F9EVOF85+h070FnkHVEoU4+rmT6Vw0bmNl7s61VEkY/cJkL7RCv1p4urnUDUMrS5rk2w==", + "dev": true, + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, "engines": { - "node": ">= 0.6" + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/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==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/nodemon/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==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, "node_modules/normalize-path": { @@ -3451,6 +3177,7 @@ "version": "1.13.1", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -3545,16 +3272,10 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } + "node_modules/ohash": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-1.1.3.tgz", + "integrity": "sha512-zuHHiGTYTA1sYJ/wZN+t5HKZaH23i4yI1HMwbuXm24Nid7Dv0KcuRlKoNKS9UNfAVSBlnGLcuQrnOKWOZoEGaw==" }, "node_modules/on-headers": { "version": "1.0.2", @@ -3695,11 +3416,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" - }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -3709,6 +3425,11 @@ "node": ">=8" } }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==" + }, "node_modules/picocolors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", @@ -3931,17 +3652,11 @@ "node": ">=0.4.0" } }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true }, "node_modules/punycode": { "version": "2.3.1", @@ -3970,20 +3685,6 @@ "node": ">=10.13.0" } }, - "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -4004,6 +3705,11 @@ } ] }, + "node_modules/radix3": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz", + "integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==" + }, "node_modules/ramda": { "version": "0.27.2", "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.2.tgz", @@ -4018,28 +3724,6 @@ "node": ">= 0.8" } }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -4245,11 +3929,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, "node_modules/semver": { "version": "7.6.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", @@ -4262,61 +3941,6 @@ "node": ">=10" } }, - "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/send/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/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -4326,6 +3950,7 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -4353,11 +3978,6 @@ "node": ">= 0.4" } }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -4383,6 +4003,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dev": true, "dependencies": { "call-bind": "^1.0.7", "es-errors": "^1.3.0", @@ -4408,6 +4029,18 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -4451,14 +4084,6 @@ "dev": true, "peer": true }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -4604,6 +4229,15 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/sucrase/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==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/sucrase/node_modules/glob": { "version": "10.4.1", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.1.tgz", @@ -4784,12 +4418,13 @@ "node": ">=8.0" } }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "engines": { - "node": ">=0.6" + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "bin": { + "nodetouch": "bin/nodetouch.js" } }, "node_modules/ts-api-utils": { @@ -4848,18 +4483,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/typed-array-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", @@ -4946,6 +4569,11 @@ "node": ">=4.2.0" } }, + "node_modules/ufo": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.3.tgz", + "integrity": "sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==" + }, "node_modules/uid-safe": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", @@ -4972,12 +4600,27 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "engines": { - "node": ">= 0.8" + "node_modules/uncrypto": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz", + "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==" + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true + }, + "node_modules/unenv": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/unenv/-/unenv-1.9.0.tgz", + "integrity": "sha512-QKnFNznRxmbOF1hDgzpqrlIf6NC5sbZ2OJ+5Wl3OX8uM+LUJXbj4TXvLJCtwbPTmbMHCLIz6JLKNinNsMShK9g==", + "dependencies": { + "consola": "^3.2.3", + "defu": "^6.1.3", + "mime": "^3.0.0", + "node-fetch-native": "^1.6.1", + "pathe": "^1.1.1" } }, "node_modules/uri-js": { @@ -4996,26 +4639,6 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/v8-compile-cache": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz", @@ -5023,14 +4646,6 @@ "dev": true, "peer": true }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/src/package.json b/src/package.json index afb896ca..8a3954d7 100644 --- a/src/package.json +++ b/src/package.json @@ -1,27 +1,27 @@ { - "release": "11", + "release": "13", "name": "wg-easy", "version": "1.0.1", "description": "The easiest way to run WireGuard VPN + Web-based Admin UI.", "main": "server.js", "scripts": { - "serve": "DEBUG=Server,WireGuard nodemon server.js", + "serve": "DEBUG=Server,WireGuard npx nodemon server.js", "serve-with-password": "PASSWORD=wg npm run serve", "lint": "eslint .", "buildcss": "npx tailwindcss -i ./www/src/css/app.css -o ./www/css/app.css" }, "author": "Emile Nijssen", - "license": "GPL", + "license": "CC BY-NC-SA 4.0", "dependencies": { "bcryptjs": "^2.4.3", "debug": "^4.3.5", - "express": "^4.19.2", "express-session": "^1.18.0", - "qrcode": "^1.5.3", - "uuid": "^10.0.0" + "h3": "^1.11.1", + "qrcode": "^1.5.3" }, "devDependencies": { "eslint-config-athom": "^3.1.3", + "nodemon": "^3.1.3", "tailwindcss": "^3.4.4" }, "nodemonConfig": { @@ -30,6 +30,6 @@ ] }, "engines": { - "node": "18" + "node": ">=18" } } diff --git a/src/server.js b/src/server.js index 0bc2c985..6584b768 100644 --- a/src/server.js +++ b/src/server.js @@ -12,3 +12,18 @@ WireGuard.getConfig() // eslint-disable-next-line no-process-exit process.exit(1); }); + +// Handle terminate signal +process.on('SIGTERM', async () => { + // eslint-disable-next-line no-console + console.log('SIGTERM signal received.'); + await WireGuard.Shutdown(); + // eslint-disable-next-line no-process-exit + process.exit(0); +}); + +// Handle interrupt signal +process.on('SIGINT', () => { + // eslint-disable-next-line no-console + console.log('SIGINT signal received.'); +}); diff --git a/src/tailwind.config.js b/src/tailwind.config.js index 0594481d..bc7b40da 100644 --- a/src/tailwind.config.js +++ b/src/tailwind.config.js @@ -3,6 +3,28 @@ 'use strict'; module.exports = { - darkMode: 'media', + darkMode: 'selector', content: ['./www/**/*.{html,js}'], + theme: { + screens: { + xxs: '450px', + xs: '576px', + sm: '640px', + md: '768px', + lg: '1024px', + xl: '1280px', + '2xl': '1536px', + }, + }, + plugins: [ + function addDisabledClass({ addUtilities }) { + const newUtilities = { + '.is-disabled': { + opacity: '0.25', + cursor: 'default', + }, + }; + addUtilities(newUtilities); + }, + ], }; diff --git a/src/www/css/app.css b/src/www/css/app.css index 32a59830..ead64396 100644 --- a/src/www/css/app.css +++ b/src/www/css/app.css @@ -558,6 +558,18 @@ video { width: 100%; } +@media (min-width: 450px) { + .container { + max-width: 450px; + } +} + +@media (min-width: 576px) { + .container { + max-width: 576px; + } +} + @media (min-width: 640px) { .container { max-width: 640px; @@ -588,6 +600,18 @@ video { } } +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} + .visible { visibility: visible; } @@ -690,8 +714,8 @@ video { margin-bottom: 2.5rem; } -.mb-2 { - margin-bottom: 0.5rem; +.mb-4 { + margin-bottom: 1rem; } .mb-5 { @@ -710,8 +734,12 @@ video { margin-right: 0.5rem; } -.mr-5 { - margin-right: 1.25rem; +.mt-0 { + margin-top: 0px; +} + +.mt-0\.5 { + margin-top: 0.125rem; } .mt-10 { @@ -726,10 +754,18 @@ video { margin-top: 0.75rem; } +.mt-4 { + margin-top: 1rem; +} + .mt-5 { margin-top: 1.25rem; } +.mt-px { + margin-top: 1px; +} + .block { display: block; } @@ -794,14 +830,26 @@ video { height: 1rem; } +.h-5 { + height: 1.25rem; +} + .h-6 { height: 1.5rem; } +.h-8 { + height: 2rem; +} + .min-h-screen { min-height: 100vh; } +.w-1 { + width: 0.25rem; +} + .w-10 { width: 2.5rem; } @@ -842,6 +890,10 @@ video { width: 100%; } +.min-w-20 { + min-width: 5rem; +} + .max-w-3xl { max-width: 48rem; } @@ -854,10 +906,18 @@ video { flex-shrink: 0; } +.shrink-0 { + flex-shrink: 0; +} + .flex-grow { flex-grow: 1; } +.grow-0 { + flex-grow: 0; +} + .transform { transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); } @@ -903,10 +963,18 @@ video { flex-direction: column; } +.flex-col-reverse { + flex-direction: column-reverse; +} + .flex-wrap { flex-wrap: wrap; } +.items-end { + align-items: flex-end; +} + .items-center { align-items: center; } @@ -927,6 +995,22 @@ video { gap: 0.25rem; } +.gap-2 { + gap: 0.5rem; +} + +.gap-3 { + gap: 0.75rem; +} + +.self-start { + align-self: flex-start; +} + +.self-end { + align-self: flex-end; +} + .overflow-hidden { overflow: hidden; } @@ -941,6 +1025,10 @@ video { white-space: nowrap; } +.whitespace-nowrap { + white-space: nowrap; +} + .rounded { border-radius: 0.25rem; } @@ -1053,6 +1141,14 @@ video { --tw-bg-opacity: 0.5; } +.fill-gray-400 { + fill: #9ca3af; +} + +.fill-gray-600 { + fill: #4b5563; +} + .p-1 { padding: 0.25rem; } @@ -1115,10 +1211,6 @@ video { padding-bottom: 3rem; } -.pb-2 { - padding-bottom: 0.5rem; -} - .pb-20 { padding-bottom: 5rem; } @@ -1241,6 +1333,11 @@ video { color: rgb(17 24 39 / var(--tw-text-opacity)); } +.text-neutral-400 { + --tw-text-opacity: 1; + color: rgb(163 163 163 / var(--tw-text-opacity)); +} + .text-red-600 { --tw-text-opacity: 1; color: rgb(220 38 38 / var(--tw-text-opacity)); @@ -1352,6 +1449,11 @@ video { transition-timing-function: cubic-bezier(0, 0, 0.2, 1); } +.is-disabled { + opacity: 0.25; + cursor: default; +} + .last\:border-b-0:last-child { border-bottom-width: 0px; } @@ -1423,6 +1525,26 @@ video { opacity: 1; } +.peer:checked ~ .peer-checked\:fill-gray-600 { + fill: #4b5563; +} + +@media (min-width: 450px) { + .xxs\:flex-row { + flex-direction: row; + } + + .xxs\:self-center { + align-self: center; + } +} + +@media (min-width: 576px) { + .xs\:mt-6 { + margin-top: 1.5rem; + } +} + @media (min-width: 640px) { .sm\:mx-0 { margin-left: 0px; @@ -1490,6 +1612,10 @@ video { transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); } + .sm\:flex-row { + flex-direction: row; + } + .sm\:flex-row-reverse { flex-direction: row-reverse; } @@ -1534,8 +1660,12 @@ video { display: inline-block; } - .md\:flex-row { - flex-direction: row; + .md\:min-w-24 { + min-width: 6rem; + } + + .md\:gap-4 { + gap: 1rem; } .md\:px-0 { @@ -1543,213 +1673,237 @@ video { padding-right: 0px; } + .md\:py-5 { + padding-top: 1.25rem; + padding-bottom: 1.25rem; + } + .md\:pb-0 { padding-bottom: 0px; } -} -@media (prefers-color-scheme: dark) { - .dark\:border-neutral-500 { - --tw-border-opacity: 1; - border-color: rgb(115 115 115 / var(--tw-border-opacity)); + .md\:text-base { + font-size: 1rem; + line-height: 1.5rem; } +} - .dark\:border-neutral-600 { - --tw-border-opacity: 1; - border-color: rgb(82 82 82 / var(--tw-border-opacity)); - } +.dark\:border-neutral-500:where(.dark, .dark *) { + --tw-border-opacity: 1; + border-color: rgb(115 115 115 / var(--tw-border-opacity)); +} - .dark\:border-neutral-800 { - --tw-border-opacity: 1; - border-color: rgb(38 38 38 / var(--tw-border-opacity)); - } +.dark\:border-neutral-600:where(.dark, .dark *) { + --tw-border-opacity: 1; + border-color: rgb(82 82 82 / var(--tw-border-opacity)); +} - .dark\:border-red-600 { - --tw-border-opacity: 1; - border-color: rgb(220 38 38 / var(--tw-border-opacity)); - } +.dark\:border-neutral-800:where(.dark, .dark *) { + --tw-border-opacity: 1; + border-color: rgb(38 38 38 / var(--tw-border-opacity)); +} - .dark\:bg-black { - --tw-bg-opacity: 1; - background-color: rgb(0 0 0 / var(--tw-bg-opacity)); - } +.dark\:border-red-600:where(.dark, .dark *) { + --tw-border-opacity: 1; + border-color: rgb(220 38 38 / var(--tw-border-opacity)); +} - .dark\:bg-neutral-400 { - --tw-bg-opacity: 1; - background-color: rgb(163 163 163 / var(--tw-bg-opacity)); - } +.dark\:bg-black:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(0 0 0 / var(--tw-bg-opacity)); +} - .dark\:bg-neutral-500 { - --tw-bg-opacity: 1; - background-color: rgb(115 115 115 / var(--tw-bg-opacity)); - } +.dark\:bg-neutral-400:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(163 163 163 / var(--tw-bg-opacity)); +} - .dark\:bg-neutral-600 { - --tw-bg-opacity: 1; - background-color: rgb(82 82 82 / var(--tw-bg-opacity)); - } +.dark\:bg-neutral-500:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(115 115 115 / var(--tw-bg-opacity)); +} - .dark\:bg-neutral-700 { - --tw-bg-opacity: 1; - background-color: rgb(64 64 64 / var(--tw-bg-opacity)); - } +.dark\:bg-neutral-600:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(82 82 82 / var(--tw-bg-opacity)); +} - .dark\:bg-neutral-800 { - --tw-bg-opacity: 1; - background-color: rgb(38 38 38 / var(--tw-bg-opacity)); - } +.dark\:bg-neutral-700:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(64 64 64 / var(--tw-bg-opacity)); +} - .dark\:bg-red-100 { - --tw-bg-opacity: 1; - background-color: rgb(254 226 226 / var(--tw-bg-opacity)); - } +.dark\:bg-neutral-800:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(38 38 38 / var(--tw-bg-opacity)); +} - .dark\:bg-red-600 { - --tw-bg-opacity: 1; - background-color: rgb(220 38 38 / var(--tw-bg-opacity)); - } +.dark\:bg-red-100:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(254 226 226 / var(--tw-bg-opacity)); +} - .dark\:bg-red-800 { - --tw-bg-opacity: 1; - background-color: rgb(153 27 27 / var(--tw-bg-opacity)); - } +.dark\:bg-red-600:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(220 38 38 / var(--tw-bg-opacity)); +} - .dark\:text-gray-500 { - --tw-text-opacity: 1; - color: rgb(107 114 128 / var(--tw-text-opacity)); - } +.dark\:bg-red-800:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(153 27 27 / var(--tw-bg-opacity)); +} - .dark\:text-neutral-200 { - --tw-text-opacity: 1; - color: rgb(229 229 229 / var(--tw-text-opacity)); - } +.dark\:fill-neutral-400:where(.dark, .dark *) { + fill: #a3a3a3; +} - .dark\:text-neutral-300 { - --tw-text-opacity: 1; - color: rgb(212 212 212 / var(--tw-text-opacity)); - } +.dark\:fill-neutral-600:where(.dark, .dark *) { + fill: #525252; +} - .dark\:text-neutral-400 { - --tw-text-opacity: 1; - color: rgb(163 163 163 / var(--tw-text-opacity)); - } +.dark\:text-gray-500:where(.dark, .dark *) { + --tw-text-opacity: 1; + color: rgb(107 114 128 / var(--tw-text-opacity)); +} - .dark\:text-neutral-50 { - --tw-text-opacity: 1; - color: rgb(250 250 250 / var(--tw-text-opacity)); - } +.dark\:text-neutral-200:where(.dark, .dark *) { + --tw-text-opacity: 1; + color: rgb(229 229 229 / var(--tw-text-opacity)); +} - .dark\:text-neutral-500 { - --tw-text-opacity: 1; - color: rgb(115 115 115 / var(--tw-text-opacity)); - } +.dark\:text-neutral-300:where(.dark, .dark *) { + --tw-text-opacity: 1; + color: rgb(212 212 212 / var(--tw-text-opacity)); +} - .dark\:text-neutral-600 { - --tw-text-opacity: 1; - color: rgb(82 82 82 / var(--tw-text-opacity)); - } +.dark\:text-neutral-400:where(.dark, .dark *) { + --tw-text-opacity: 1; + color: rgb(163 163 163 / var(--tw-text-opacity)); +} - .dark\:text-red-300 { - --tw-text-opacity: 1; - color: rgb(252 165 165 / var(--tw-text-opacity)); - } +.dark\:text-neutral-50:where(.dark, .dark *) { + --tw-text-opacity: 1; + color: rgb(250 250 250 / var(--tw-text-opacity)); +} - .dark\:text-red-600 { - --tw-text-opacity: 1; - color: rgb(220 38 38 / var(--tw-text-opacity)); - } +.dark\:text-neutral-500:where(.dark, .dark *) { + --tw-text-opacity: 1; + color: rgb(115 115 115 / var(--tw-text-opacity)); +} - .dark\:text-white { - --tw-text-opacity: 1; - color: rgb(255 255 255 / var(--tw-text-opacity)); - } +.dark\:text-neutral-600:where(.dark, .dark *) { + --tw-text-opacity: 1; + color: rgb(82 82 82 / var(--tw-text-opacity)); +} - .dark\:opacity-50 { - opacity: 0.5; - } +.dark\:text-red-300:where(.dark, .dark *) { + --tw-text-opacity: 1; + color: rgb(252 165 165 / var(--tw-text-opacity)); +} - .dark\:placeholder\:text-neutral-400::-moz-placeholder { - --tw-text-opacity: 1; - color: rgb(163 163 163 / var(--tw-text-opacity)); - } +.dark\:text-red-600:where(.dark, .dark *) { + --tw-text-opacity: 1; + color: rgb(220 38 38 / var(--tw-text-opacity)); +} - .dark\:placeholder\:text-neutral-400::placeholder { - --tw-text-opacity: 1; - color: rgb(163 163 163 / var(--tw-text-opacity)); - } +.dark\:text-white:where(.dark, .dark *) { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); +} - .dark\:placeholder\:text-neutral-500::-moz-placeholder { - --tw-text-opacity: 1; - color: rgb(115 115 115 / var(--tw-text-opacity)); - } +.dark\:opacity-50:where(.dark, .dark *) { + opacity: 0.5; +} - .dark\:placeholder\:text-neutral-500::placeholder { - --tw-text-opacity: 1; - color: rgb(115 115 115 / var(--tw-text-opacity)); - } +.dark\:placeholder\:text-neutral-400:where(.dark, .dark *)::-moz-placeholder { + --tw-text-opacity: 1; + color: rgb(163 163 163 / var(--tw-text-opacity)); +} - .dark\:hover\:border-neutral-600:hover { - --tw-border-opacity: 1; - border-color: rgb(82 82 82 / var(--tw-border-opacity)); - } +.dark\:placeholder\:text-neutral-400:where(.dark, .dark *)::placeholder { + --tw-text-opacity: 1; + color: rgb(163 163 163 / var(--tw-text-opacity)); +} - .dark\:hover\:border-red-600:hover { - --tw-border-opacity: 1; - border-color: rgb(220 38 38 / var(--tw-border-opacity)); - } +.dark\:placeholder\:text-neutral-500:where(.dark, .dark *)::-moz-placeholder { + --tw-text-opacity: 1; + color: rgb(115 115 115 / var(--tw-text-opacity)); +} - .dark\:hover\:bg-neutral-500:hover { - --tw-bg-opacity: 1; - background-color: rgb(115 115 115 / var(--tw-bg-opacity)); - } +.dark\:placeholder\:text-neutral-500:where(.dark, .dark *)::placeholder { + --tw-text-opacity: 1; + color: rgb(115 115 115 / var(--tw-text-opacity)); +} - .dark\:hover\:bg-neutral-600:hover { - --tw-bg-opacity: 1; - background-color: rgb(82 82 82 / var(--tw-bg-opacity)); - } +.dark\:hover\:border-neutral-600:hover:where(.dark, .dark *) { + --tw-border-opacity: 1; + border-color: rgb(82 82 82 / var(--tw-border-opacity)); +} - .dark\:hover\:bg-red-600:hover { - --tw-bg-opacity: 1; - background-color: rgb(220 38 38 / var(--tw-bg-opacity)); - } +.dark\:hover\:border-red-600:hover:where(.dark, .dark *) { + --tw-border-opacity: 1; + border-color: rgb(220 38 38 / var(--tw-border-opacity)); +} - .dark\:hover\:bg-red-700:hover { - --tw-bg-opacity: 1; - background-color: rgb(185 28 28 / var(--tw-bg-opacity)); - } +.dark\:hover\:bg-neutral-500:hover:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(115 115 115 / var(--tw-bg-opacity)); +} - .dark\:hover\:bg-red-800:hover { - --tw-bg-opacity: 1; - background-color: rgb(153 27 27 / var(--tw-bg-opacity)); - } +.dark\:hover\:bg-neutral-600:hover:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(82 82 82 / var(--tw-bg-opacity)); +} - .dark\:hover\:text-neutral-700:hover { - --tw-text-opacity: 1; - color: rgb(64 64 64 / var(--tw-text-opacity)); - } +.dark\:hover\:bg-red-600:hover:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(220 38 38 / var(--tw-bg-opacity)); +} - .dark\:hover\:text-red-100:hover { - --tw-text-opacity: 1; - color: rgb(254 226 226 / var(--tw-text-opacity)); - } +.dark\:hover\:bg-red-700:hover:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(185 28 28 / var(--tw-bg-opacity)); +} - .dark\:hover\:text-white:hover { - --tw-text-opacity: 1; - color: rgb(255 255 255 / var(--tw-text-opacity)); - } +.dark\:hover\:bg-red-800:hover:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(153 27 27 / var(--tw-bg-opacity)); +} - .dark\:focus\:border-neutral-500:focus { - --tw-border-opacity: 1; - border-color: rgb(115 115 115 / var(--tw-border-opacity)); - } +.dark\:hover\:text-neutral-700:hover:where(.dark, .dark *) { + --tw-text-opacity: 1; + color: rgb(64 64 64 / var(--tw-text-opacity)); +} - .dark\:focus\:border-red-800:focus { - --tw-border-opacity: 1; - border-color: rgb(153 27 27 / var(--tw-border-opacity)); - } +.dark\:hover\:text-red-100:hover:where(.dark, .dark *) { + --tw-text-opacity: 1; + color: rgb(254 226 226 / var(--tw-text-opacity)); +} - .focus\:dark\:border-neutral-500:focus { - --tw-border-opacity: 1; - border-color: rgb(115 115 115 / var(--tw-border-opacity)); - } +.dark\:hover\:text-white:hover:where(.dark, .dark *) { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); +} + +.dark\:focus\:border-neutral-500:focus:where(.dark, .dark *) { + --tw-border-opacity: 1; + border-color: rgb(115 115 115 / var(--tw-border-opacity)); +} + +.dark\:focus\:border-red-800:focus:where(.dark, .dark *) { + --tw-border-opacity: 1; + border-color: rgb(153 27 27 / var(--tw-border-opacity)); +} + +.focus\:dark\:border-neutral-500:where(.dark, .dark *):focus { + --tw-border-opacity: 1; + border-color: rgb(115 115 115 / var(--tw-border-opacity)); +} + +.group:hover .group-hover\:dark\:fill-neutral-500:where(.dark, .dark *) { + fill: #737373; +} + +.peer:checked ~ .peer-checked\:dark\:fill-neutral-400:where(.dark, .dark *) { + fill: #a3a3a3; } diff --git a/src/www/index.html b/src/www/index.html index 5f8c653f..72044728 100644 --- a/src/www/index.html +++ b/src/www/index.html @@ -3,6 +3,7 @@