forked from mirrors/amnezia-wg-easy
		
	refactor!: migrate from express to h3
This commit is contained in:
		
							parent
							
								
									f963e7a4d1
								
							
						
					
					
						commit
						e8a160b14f
					
				
					 5 changed files with 256 additions and 90 deletions
				
			
		| 
						 | 
				
			
			@ -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 { join } = 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 {
 | 
			
		||||
| 
						 | 
				
			
			@ -23,33 +35,39 @@ const {
 | 
			
		|||
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,
 | 
			
		||||
      cookie: {
 | 
			
		||||
        httpOnly: true,
 | 
			
		||||
      },
 | 
			
		||||
    })));
 | 
			
		||||
 | 
			
		||||
    const router = createRouter();
 | 
			
		||||
    app.use(router);
 | 
			
		||||
 | 
			
		||||
    router
 | 
			
		||||
      .get('/api/release', defineEventHandler((event) => {
 | 
			
		||||
        setHeader(event, 'Content-Type', 'application/json');
 | 
			
		||||
 | 
			
		||||
        return RELEASE;
 | 
			
		||||
      }))
 | 
			
		||||
 | 
			
		||||
      .get('/api/release', (Util.promisify(async () => {
 | 
			
		||||
        return RELEASE;
 | 
			
		||||
      })))
 | 
			
		||||
      .get('/api/lang', defineEventHandler((event) => {
 | 
			
		||||
        setHeader(event, 'Content-Type', 'application/json');
 | 
			
		||||
 | 
			
		||||
      .get('/api/lang', (Util.promisify(async () => {
 | 
			
		||||
        return LANG;
 | 
			
		||||
      })))
 | 
			
		||||
        return `"${LANG}"`;
 | 
			
		||||
      }))
 | 
			
		||||
 | 
			
		||||
    // Authentication
 | 
			
		||||
      .get('/api/session', Util.promisify(async (req) => {
 | 
			
		||||
      .get('/api/session', defineEventHandler((event) => {
 | 
			
		||||
        const requiresPassword = !!process.env.PASSWORD;
 | 
			
		||||
        const authenticated = requiresPassword
 | 
			
		||||
          ? !!(req.session && req.session.authenticated)
 | 
			
		||||
          ? !!(event.node.req.session && event.node.req.session.authenticated)
 | 
			
		||||
          : true;
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
| 
						 | 
				
			
			@ -57,28 +75,35 @@ 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);
 | 
			
		||||
          throw createError({
 | 
			
		||||
            status: 401,
 | 
			
		||||
            message: 'Missing: Password',
 | 
			
		||||
          });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (password !== PASSWORD) {
 | 
			
		||||
          throw new ServerError('Incorrect Password', 401);
 | 
			
		||||
          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 { succcess: true };
 | 
			
		||||
      }));
 | 
			
		||||
 | 
			
		||||
    // WireGuard
 | 
			
		||||
      .use((req, res, next) => {
 | 
			
		||||
        if (!PASSWORD) {
 | 
			
		||||
    app.use(
 | 
			
		||||
      fromNodeMiddleware((req, res, next) => {
 | 
			
		||||
        if (!PASSWORD || !req.url.startsWith('/api/')) {
 | 
			
		||||
          return next();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -86,7 +111,7 @@ module.exports = class Server {
 | 
			
		|||
          return next();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (req.path.startsWith('/api/') && req.headers['authorization']) {
 | 
			
		||||
        if (req.url.startsWith('/api/') && req.headers['authorization']) {
 | 
			
		||||
          if (bcrypt.compareSync(req.headers['authorization'], bcrypt.hashSync(PASSWORD, 10))) {
 | 
			
		||||
            return next();
 | 
			
		||||
          }
 | 
			
		||||
| 
						 | 
				
			
			@ -98,25 +123,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 +156,79 @@ 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 };
 | 
			
		||||
      }));
 | 
			
		||||
 | 
			
		||||
      .listen(PORT, WEBUI_HOST, () => {
 | 
			
		||||
        debug(`Listening on http://${WEBUI_HOST}:${PORT}`);
 | 
			
		||||
      });
 | 
			
		||||
    // Static assets
 | 
			
		||||
    const publicDir = 'www';
 | 
			
		||||
    app.use(
 | 
			
		||||
      defineEventHandler((event) => {
 | 
			
		||||
        return serveStatic(event, {
 | 
			
		||||
          getContents: (id) => readFile(join(publicDir, id)),
 | 
			
		||||
          getMeta: async (id) => {
 | 
			
		||||
            const stats = await stat(join(publicDir, id)).catch(() => {});
 | 
			
		||||
 | 
			
		||||
            if (!stats || !stats.isFile()) {
 | 
			
		||||
              return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
              size: stats.size,
 | 
			
		||||
              mtime: stats.mtimeMs,
 | 
			
		||||
            };
 | 
			
		||||
          },
 | 
			
		||||
        });
 | 
			
		||||
      }),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    createServer(toNodeListener(app)).listen(PORT, WEBUI_HOST);
 | 
			
		||||
    debug(`Listening on http://${WEBUI_HOST}:${PORT}`);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,10 +0,0 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
module.exports = class ServerError extends Error {
 | 
			
		||||
 | 
			
		||||
  constructor(message, statusCode = 500) {
 | 
			
		||||
    super(message);
 | 
			
		||||
    this.statusCode = statusCode;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										115
									
								
								src/package-lock.json
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										115
									
								
								src/package-lock.json
									
										
									
										generated
									
									
									
								
							| 
						 | 
				
			
			@ -13,6 +13,7 @@
 | 
			
		|||
        "debug": "^4.3.4",
 | 
			
		||||
        "express": "^4.18.2",
 | 
			
		||||
        "express-session": "^1.18.0",
 | 
			
		||||
        "h3": "^1.11.1",
 | 
			
		||||
        "qrcode": "^1.5.3",
 | 
			
		||||
        "uuid": "^9.0.1"
 | 
			
		||||
      },
 | 
			
		||||
| 
						 | 
				
			
			@ -1200,6 +1201,14 @@
 | 
			
		|||
      "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==",
 | 
			
		||||
      "dev": true
 | 
			
		||||
    },
 | 
			
		||||
    "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": "^14.18.0 || >=16.10.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/content-disposition": {
 | 
			
		||||
      "version": "0.5.4",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
 | 
			
		||||
| 
						 | 
				
			
			@ -1227,6 +1236,11 @@
 | 
			
		|||
        "node": ">= 0.6"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/cookie-es": {
 | 
			
		||||
      "version": "1.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.0.0.tgz",
 | 
			
		||||
      "integrity": "sha512-mWYvfOLrfEc996hlKcdABeIiPHUPC6DM2QYZdGGOvhOTbA3tjm2eBwqlJpoFdjC89NI4Qt6h0Pu06Mp+1Pj5OQ=="
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/cookie-signature": {
 | 
			
		||||
      "version": "1.0.6",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
 | 
			
		||||
| 
						 | 
				
			
			@ -1246,6 +1260,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",
 | 
			
		||||
| 
						 | 
				
			
			@ -1322,6 +1349,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",
 | 
			
		||||
| 
						 | 
				
			
			@ -1330,6 +1362,11 @@
 | 
			
		|||
        "node": ">= 0.8"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/destr": {
 | 
			
		||||
      "version": "2.0.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.3.tgz",
 | 
			
		||||
      "integrity": "sha512-2N3BOUU4gYMpTP24s5rF5iP7BDr7uNTCs4ozw3kf/eKfvWSIu93GEBi5m427YoyJoeOzQ5smuu4nNAPGb8idSQ=="
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/destroy": {
 | 
			
		||||
      "version": "1.2.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
 | 
			
		||||
| 
						 | 
				
			
			@ -2623,6 +2660,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",
 | 
			
		||||
| 
						 | 
				
			
			@ -2801,6 +2855,14 @@
 | 
			
		|||
        "node": ">= 0.10"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/iron-webcrypto": {
 | 
			
		||||
      "version": "1.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.0.0.tgz",
 | 
			
		||||
      "integrity": "sha512-anOK1Mktt8U1Xi7fCM3RELTuYbnFikQY5VtrDj7kPgpejV7d43tWKhzgioO0zpkazLEL/j/iayRqnJhrGfqUsg==",
 | 
			
		||||
      "funding": {
 | 
			
		||||
        "url": "https://github.com/sponsors/brc-dd"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/is-array-buffer": {
 | 
			
		||||
      "version": "3.0.4",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz",
 | 
			
		||||
| 
						 | 
				
			
			@ -3372,6 +3434,11 @@
 | 
			
		|||
        "node": ">= 0.6"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/node-fetch-native": {
 | 
			
		||||
      "version": "1.6.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.2.tgz",
 | 
			
		||||
      "integrity": "sha512-69mtXOFZ6hSkYiXAVB5SqaRvrbITC/NPyqv7yuu/qw0nmgPyYbIMYYNIDhNtwPrzk0ptrimrLz/hhjvm4w5Z+w=="
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/normalize-path": {
 | 
			
		||||
      "version": "3.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
 | 
			
		||||
| 
						 | 
				
			
			@ -3495,6 +3562,11 @@
 | 
			
		|||
        "url": "https://github.com/sponsors/ljharb"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "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-finished": {
 | 
			
		||||
      "version": "2.4.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
 | 
			
		||||
| 
						 | 
				
			
			@ -3668,6 +3740,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.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
 | 
			
		||||
| 
						 | 
				
			
			@ -3963,6 +4040,11 @@
 | 
			
		|||
        }
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/radix3": {
 | 
			
		||||
      "version": "1.1.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.0.tgz",
 | 
			
		||||
      "integrity": "sha512-pNsHDxbGORSvuSScqNJ+3Km6QAVqk8CfsCBIEoDgpqLrkD2f3QM4I7d1ozJJ172OmIcoUcerZaNWqtLkRXTV3A=="
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/ramda": {
 | 
			
		||||
      "version": "0.27.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.2.tgz",
 | 
			
		||||
| 
						 | 
				
			
			@ -4888,6 +4970,11 @@
 | 
			
		|||
        "node": ">=4.2.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/ufo": {
 | 
			
		||||
      "version": "1.4.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.4.0.tgz",
 | 
			
		||||
      "integrity": "sha512-Hhy+BhRBleFjpJ2vchUNN40qgkh0366FWJGqVLYBHev0vpHTrXSA0ryT+74UiW6KWsldNurQMKGqCm1M2zBciQ=="
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/uid-safe": {
 | 
			
		||||
      "version": "2.1.5",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
 | 
			
		||||
| 
						 | 
				
			
			@ -4914,6 +5001,34 @@
 | 
			
		|||
        "url": "https://github.com/sponsors/ljharb"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/uncrypto": {
 | 
			
		||||
      "version": "0.1.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz",
 | 
			
		||||
      "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q=="
 | 
			
		||||
    },
 | 
			
		||||
    "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/unenv/node_modules/mime": {
 | 
			
		||||
      "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": ">=10.0.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/unpipe": {
 | 
			
		||||
      "version": "1.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,6 +17,7 @@
 | 
			
		|||
    "debug": "^4.3.4",
 | 
			
		||||
    "express": "^4.18.2",
 | 
			
		||||
    "express-session": "^1.18.0",
 | 
			
		||||
    "h3": "^1.11.1",
 | 
			
		||||
    "qrcode": "^1.5.3",
 | 
			
		||||
    "uuid": "^9.0.1"
 | 
			
		||||
  },
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,7 @@
 | 
			
		|||
 | 
			
		||||
<head>
 | 
			
		||||
  <title>WireGuard</title>
 | 
			
		||||
  <meta charset="utf-8"/>
 | 
			
		||||
  <link href="./css/app.css" rel="stylesheet">
 | 
			
		||||
  <link rel="manifest" href="./manifest.json">
 | 
			
		||||
  <link rel="icon" type="image/png" href="./img/favicon.png">
 | 
			
		||||
| 
						 | 
				
			
			@ -490,7 +491,7 @@
 | 
			
		|||
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <p v-cloak class="text-center m-10 text-gray-300 dark:text-neutral-600 text-xs"> <a class="hover:underline" target="_blank" 
 | 
			
		||||
    <p v-cloak class="text-center m-10 text-gray-300 dark:text-neutral-600 text-xs"> <a class="hover:underline" target="_blank"
 | 
			
		||||
        href="https://github.com/wg-easy/wg-easy">WireGuard Easy</a> © 2021-2024 by <a class="hover:underline" target="_blank"
 | 
			
		||||
        href="https://emilenijssen.nl/?ref=wg-easy">Emile Nijssen</a> is licensed under <a class="hover:underline" target="_blank"
 | 
			
		||||
        href="http://creativecommons.org/licenses/by-nc-sa/4.0/">CC BY-NC-SA 4.0</a> · <a class="hover:underline"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue