Browse Source

add prettier, format

pull/1250/head
Bernd Storath 9 months ago
parent
commit
de2ee3dc58
  1. 6
      src/.prettierrc.json
  2. 1959
      src/app.vue
  3. 10
      src/assets/css/app.css
  4. 4
      src/eslint.config.mjs
  5. 1190
      src/i18n.config.ts
  6. 8
      src/middleware/session.ts
  7. 4
      src/nuxt.config.ts
  8. 4
      src/package.json
  9. 4
      src/plugins/apexcharts.client.ts
  10. 6219
      src/pnpm-lock.yaml
  11. 20
      src/public/manifest.json
  12. 10
      src/server/api/lang.ts
  13. 10
      src/server/api/release.ts
  14. 100
      src/server/api/session.ts
  15. 10
      src/server/api/ui-chart-type.ts
  16. 10
      src/server/api/ui-traffic-stats.ts
  17. 14
      src/server/api/wireguard/backup.ts
  18. 24
      src/server/api/wireguard/client/[clientId]/address.ts
  19. 32
      src/server/api/wireguard/client/[clientId]/configuration.ts
  20. 22
      src/server/api/wireguard/client/[clientId]/disable.ts
  21. 22
      src/server/api/wireguard/client/[clientId]/enable.ts
  22. 12
      src/server/api/wireguard/client/[clientId]/index.ts
  23. 24
      src/server/api/wireguard/client/[clientId]/name.ts
  24. 14
      src/server/api/wireguard/client/[clientId]/qrcode.svg.ts
  25. 18
      src/server/api/wireguard/client/index.ts
  26. 12
      src/server/api/wireguard/restore.ts
  27. 5
      src/tailwind.config.ts
  28. 99
      src/utils/WireGuard.ts
  29. 98
      src/utils/api.ts
  30. 39
      src/utils/cmd.ts
  31. 35
      src/utils/config.ts
  32. 10
      src/utils/crypto.ts
  33. 18
      src/utils/ip.ts
  34. 47
      src/utils/localStorage.ts
  35. 20
      src/utils/password.ts
  36. 7
      src/wgpw.js

6
src/.prettierrc.json

@ -0,0 +1,6 @@
{
"trailingComma": "es5",
"tabWidth": 2,
"semi": true,
"singleQuote": true
}

1959
src/app.vue

File diff suppressed because it is too large

10
src/assets/css/app.css

@ -1,6 +1,6 @@
[v-cloak] {
display: none;
}
.line-chart .apexcharts-svg{
transform: translateY(3px);
}
display: none;
}
.line-chart .apexcharts-svg {
transform: translateY(3px);
}

4
src/eslint.config.mjs

@ -1,3 +1,3 @@
import { createConfigForNuxt } from '@nuxt/eslint-config/flat'
import { createConfigForNuxt } from '@nuxt/eslint-config/flat';
export default createConfigForNuxt({})
export default createConfigForNuxt({});

1190
src/i18n.config.ts

File diff suppressed because it is too large

8
src/middleware/session.ts

@ -1,5 +1,5 @@
export default defineNuxtRouteMiddleware(async (to) => {
if (REQUIRES_PASSWORD || !to.path.startsWith('/api/')) {
return abortNavigation();
}
})
if (REQUIRES_PASSWORD || !to.path.startsWith('/api/')) {
return abortNavigation();
}
});

4
src/nuxt.config.ts

@ -2,5 +2,5 @@
export default defineNuxtConfig({
compatibilityDate: '2024-04-03',
devtools: { enabled: true },
modules: ["@nuxtjs/i18n", "@nuxtjs/tailwindcss"]
})
modules: ['@nuxtjs/i18n', '@nuxtjs/tailwindcss'],
});

4
src/package.json

@ -13,7 +13,8 @@
"generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare",
"lint": "eslint ."
"lint": "eslint .",
"format": "prettier . --write"
},
"dependencies": {
"@nuxtjs/i18n": "^8.3.3",
@ -34,6 +35,7 @@
"@types/debug": "^4.1.12",
"@types/qrcode": "^1.5.5",
"eslint": "^9.8.0",
"prettier": "^3.3.3",
"typescript": "^5.5.4",
"vue-tsc": "^2.0.29"
},

4
src/plugins/apexcharts.client.ts

@ -1,5 +1,5 @@
import VueApexCharts from "vue3-apexcharts";
import VueApexCharts from 'vue3-apexcharts';
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.use(VueApexCharts);
});
});

6219
src/pnpm-lock.yaml

File diff suppressed because it is too large

20
src/public/manifest.json

@ -1,11 +1,11 @@
{
"name": "WireGuard",
"display": "standalone",
"background_color": "#fff",
"icons": [
{
"src": "/favicon.png",
"type": "image/png"
}
]
}
"name": "WireGuard",
"display": "standalone",
"background_color": "#fff",
"icons": [
{
"src": "/favicon.png",
"type": "image/png"
}
]
}

10
src/server/api/lang.ts

@ -1,7 +1,7 @@
import { LANG } from "~/utils/config";
import { LANG } from '~/utils/config';
export default defineEventHandler((event) => {
assertMethod(event, "GET");
setHeader(event, 'Content-Type', 'application/json');
return `"${LANG}"`;
})
assertMethod(event, 'GET');
setHeader(event, 'Content-Type', 'application/json');
return `"${LANG}"`;
});

10
src/server/api/release.ts

@ -1,7 +1,7 @@
import { RELEASE } from "~/utils/config";
import { RELEASE } from '~/utils/config';
export default defineEventHandler((event) => {
assertMethod(event, "GET");
setHeader(event, 'Content-Type', 'application/json');
return RELEASE;
})
assertMethod(event, 'GET');
setHeader(event, 'Content-Type', 'application/json');
return RELEASE;
});

100
src/server/api/session.ts

@ -1,51 +1,55 @@
import { REQUIRES_PASSWORD, SERVER_DEBUG, SESSION_CONFIG } from "~/utils/config";
import { isPasswordValid } from "~/utils/password";
import {
REQUIRES_PASSWORD,
SERVER_DEBUG,
SESSION_CONFIG,
} from '~/utils/config';
import { isPasswordValid } from '~/utils/password';
export default defineEventHandler(async (event) => {
if (isMethod(event, "GET")) {
const session = await useSession(event, SESSION_CONFIG);
const authenticated = REQUIRES_PASSWORD
? !!(session.data && session.data.authenticated)
: true;
return {
REQUIRES_PASSWORD,
authenticated,
};
} else if (isMethod(event, "POST")) {
const session = await useSession(event, SESSION_CONFIG);
const { password } = await readBody(event);
if (!REQUIRES_PASSWORD) {
// 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 (!isPasswordValid(password)) {
throw createError({
status: 401,
message: 'Incorrect Password',
});
}
const data = await session.update({
authenticated: true
});
SERVER_DEBUG(`New Session: ${data.id}`);
return { success: true };
} else if (isMethod(event, "DELETE")) {
const session = await useSession(event, SESSION_CONFIG);
const sessionId = session.id;
await session.clear();
SERVER_DEBUG(`Deleted Session: ${sessionId}`);
return { success: true };
if (isMethod(event, 'GET')) {
const session = await useSession(event, SESSION_CONFIG);
const authenticated = REQUIRES_PASSWORD
? !!(session.data && session.data.authenticated)
: true;
return {
REQUIRES_PASSWORD,
authenticated,
};
} else if (isMethod(event, 'POST')) {
const session = await useSession(event, SESSION_CONFIG);
const { password } = await readBody(event);
if (!REQUIRES_PASSWORD) {
// 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 (!isPasswordValid(password)) {
throw createError({
status: 401,
message: 'Incorrect Password',
});
}
const data = await session.update({
authenticated: true,
});
SERVER_DEBUG(`New Session: ${data.id}`);
return { success: true };
} else if (isMethod(event, 'DELETE')) {
const session = await useSession(event, SESSION_CONFIG);
const sessionId = session.id;
await session.clear();
SERVER_DEBUG(`Deleted Session: ${sessionId}`);
return { success: true };
}
});

10
src/server/api/ui-chart-type.ts

@ -1,7 +1,7 @@
import { UI_CHART_TYPE } from "~/utils/config";
import { UI_CHART_TYPE } from '~/utils/config';
export default defineEventHandler((event) => {
assertMethod(event, "GET");
setHeader(event, 'Content-Type', 'application/json');
return `"${UI_CHART_TYPE}"`;
})
assertMethod(event, 'GET');
setHeader(event, 'Content-Type', 'application/json');
return `"${UI_CHART_TYPE}"`;
});

10
src/server/api/ui-traffic-stats.ts

@ -1,7 +1,7 @@
import { UI_TRAFFIC_STATS } from "~/utils/config";
import { UI_TRAFFIC_STATS } from '~/utils/config';
export default defineEventHandler((event) => {
assertMethod(event, "GET");
setHeader(event, 'Content-Type', 'application/json');
return `"${UI_TRAFFIC_STATS}"`;
})
assertMethod(event, 'GET');
setHeader(event, 'Content-Type', 'application/json');
return `"${UI_TRAFFIC_STATS}"`;
});

14
src/server/api/wireguard/backup.ts

@ -1,9 +1,9 @@
import WireGuard from "~/utils/WireGuard";
import WireGuard from '~/utils/WireGuard';
export default defineEventHandler(async (event) => {
assertMethod(event, "GET");
const config = await WireGuard.backupConfiguration();
setHeader(event, 'Content-Disposition', 'attachment; filename="wg0.json"');
setHeader(event, 'Content-Type', 'text/json');
return config;
})
assertMethod(event, 'GET');
const config = await WireGuard.backupConfiguration();
setHeader(event, 'Content-Disposition', 'attachment; filename="wg0.json"');
setHeader(event, 'Content-Type', 'text/json');
return config;
});

24
src/server/api/wireguard/client/[clientId]/address.ts

@ -1,12 +1,16 @@
import WireGuard from "~/utils/WireGuard";
import WireGuard from '~/utils/WireGuard';
export default defineEventHandler(async (event) => {
assertMethod(event, "PUT");
const clientId = getRouterParam(event, 'clientId');
if (clientId === '__proto__' || clientId === 'constructor' || clientId === 'prototype') {
throw createError({ status: 403 });
}
const { address } = await readBody(event);
await WireGuard.updateClientAddress({ clientId, address });
return { success: true };
})
assertMethod(event, 'PUT');
const clientId = getRouterParam(event, 'clientId');
if (
clientId === '__proto__' ||
clientId === 'constructor' ||
clientId === 'prototype'
) {
throw createError({ status: 403 });
}
const { address } = await readBody(event);
await WireGuard.updateClientAddress({ clientId, address });
return { success: true };
});

32
src/server/api/wireguard/client/[clientId]/configuration.ts

@ -1,16 +1,20 @@
import WireGuard from "~/utils/WireGuard";
import WireGuard from '~/utils/WireGuard';
export default defineEventHandler(async (event) => {
assertMethod(event, "GET");
const clientId = getRouterParam(event, 'clientId');
const client = await WireGuard.getClient({ clientId });
const config = await WireGuard.getClientConfiguration({ clientId });
const configName = client.name
.replace(/[^a-zA-Z0-9_=+.-]/g, '-')
.replace(/(-{2,}|-$)/g, '-')
.replace(/-$/, '')
.substring(0, 32);
setHeader(event, 'Content-Disposition', `attachment; filename="${configName || clientId}.conf"`);
setHeader(event, 'Content-Type', 'text/plain');
return config;
})
assertMethod(event, 'GET');
const clientId = getRouterParam(event, 'clientId');
const client = await WireGuard.getClient({ clientId });
const config = await WireGuard.getClientConfiguration({ clientId });
const configName = client.name
.replace(/[^a-zA-Z0-9_=+.-]/g, '-')
.replace(/(-{2,}|-$)/g, '-')
.replace(/-$/, '')
.substring(0, 32);
setHeader(
event,
'Content-Disposition',
`attachment; filename="${configName || clientId}.conf"`
);
setHeader(event, 'Content-Type', 'text/plain');
return config;
});

22
src/server/api/wireguard/client/[clientId]/disable.ts

@ -1,11 +1,15 @@
import WireGuard from "~/utils/WireGuard";
import WireGuard from '~/utils/WireGuard';
export default defineEventHandler(async (event) => {
assertMethod(event, "POST");
const clientId = getRouterParam(event, 'clientId');
if (clientId === '__proto__' || clientId === 'constructor' || clientId === 'prototype') {
throw createError({ status: 403 });
}
await WireGuard.disableClient({ clientId });
return { success: true };
})
assertMethod(event, 'POST');
const clientId = getRouterParam(event, 'clientId');
if (
clientId === '__proto__' ||
clientId === 'constructor' ||
clientId === 'prototype'
) {
throw createError({ status: 403 });
}
await WireGuard.disableClient({ clientId });
return { success: true };
});

22
src/server/api/wireguard/client/[clientId]/enable.ts

@ -1,11 +1,15 @@
import WireGuard from "~/utils/WireGuard";
import WireGuard from '~/utils/WireGuard';
export default defineEventHandler(async (event) => {
assertMethod(event, "POST");
const clientId = getRouterParam(event, 'clientId');
if (clientId === '__proto__' || clientId === 'constructor' || clientId === 'prototype') {
throw createError({ status: 403 });
}
await WireGuard.enableClient({ clientId });
return { success: true };
})
assertMethod(event, 'POST');
const clientId = getRouterParam(event, 'clientId');
if (
clientId === '__proto__' ||
clientId === 'constructor' ||
clientId === 'prototype'
) {
throw createError({ status: 403 });
}
await WireGuard.enableClient({ clientId });
return { success: true };
});

12
src/server/api/wireguard/client/[clientId]/index.ts

@ -1,8 +1,8 @@
import WireGuard from "~/utils/WireGuard";
import WireGuard from '~/utils/WireGuard';
export default defineEventHandler(async (event) => {
assertMethod(event, "DELETE");
const clientId = getRouterParam(event, 'clientId');
await WireGuard.deleteClient({ clientId });
return { success: true };
});
assertMethod(event, 'DELETE');
const clientId = getRouterParam(event, 'clientId');
await WireGuard.deleteClient({ clientId });
return { success: true };
});

24
src/server/api/wireguard/client/[clientId]/name.ts

@ -1,12 +1,16 @@
import WireGuard from "~/utils/WireGuard";
import WireGuard from '~/utils/WireGuard';
export default defineEventHandler(async (event) => {
assertMethod(event, "PUT");
const clientId = getRouterParam(event, 'clientId');
if (clientId === '__proto__' || clientId === 'constructor' || clientId === 'prototype') {
throw createError({ status: 403 });
}
const { name } = await readBody(event);
await WireGuard.updateClientName({ clientId, name });
return { success: true };
})
assertMethod(event, 'PUT');
const clientId = getRouterParam(event, 'clientId');
if (
clientId === '__proto__' ||
clientId === 'constructor' ||
clientId === 'prototype'
) {
throw createError({ status: 403 });
}
const { name } = await readBody(event);
await WireGuard.updateClientName({ clientId, name });
return { success: true };
});

14
src/server/api/wireguard/client/[clientId]/qrcode.svg.ts

@ -1,9 +1,9 @@
import WireGuard from "~/utils/WireGuard";
import WireGuard from '~/utils/WireGuard';
export default defineEventHandler(async (event) => {
assertMethod(event, "GET");
const clientId = getRouterParam(event, 'clientId');
const svg = await WireGuard.getClientQRCodeSVG({ clientId });
setHeader(event, 'Content-Type', 'image/svg+xml');
return svg;
})
assertMethod(event, 'GET');
const clientId = getRouterParam(event, 'clientId');
const svg = await WireGuard.getClientQRCodeSVG({ clientId });
setHeader(event, 'Content-Type', 'image/svg+xml');
return svg;
});

18
src/server/api/wireguard/client/index.ts

@ -1,11 +1,11 @@
import WireGuard from "~/utils/WireGuard";
import WireGuard from '~/utils/WireGuard';
export default defineEventHandler(async (event) => {
if (isMethod(event, "GET")) {
return WireGuard.getClients();
} else if (isMethod(event, "POST")) {
const { name } = await readBody(event);
await WireGuard.createClient({ name });
return { success: true };
}
})
if (isMethod(event, 'GET')) {
return WireGuard.getClients();
} else if (isMethod(event, 'POST')) {
const { name } = await readBody(event);
await WireGuard.createClient({ name });
return { success: true };
}
});

12
src/server/api/wireguard/restore.ts

@ -1,8 +1,8 @@
import WireGuard from "~/utils/WireGuard";
import WireGuard from '~/utils/WireGuard';
export default defineEventHandler(async (event) => {
assertMethod(event, "PUT");
const { file } = await readBody(event);
await WireGuard.restoreConfiguration(file);
return { success: true };
})
assertMethod(event, 'PUT');
const { file } = await readBody(event);
await WireGuard.restoreConfiguration(file);
return { success: true };
});

5
src/tailwind.config.ts

@ -1,4 +1,4 @@
import type { Config } from 'tailwindcss'
import type { Config } from 'tailwindcss';
export default {
content: [],
@ -24,5 +24,4 @@ export default {
addUtilities(newUtilities);
},
],
} satisfies Config
} satisfies Config;

99
src/utils/WireGuard.ts

@ -1,13 +1,27 @@
import fs from 'node:fs/promises';
import path from 'path';
import debug_logger from 'debug'
import debug_logger from 'debug';
import crypto from 'node:crypto';
import QRCode from 'qrcode';
import { WG_PATH, WG_HOST, WG_PORT, WG_CONFIG_PORT, WG_MTU, WG_DEFAULT_DNS, WG_DEFAULT_ADDRESS, WG_PERSISTENT_KEEPALIVE, WG_ALLOWED_IPS, WG_PRE_UP, WG_POST_UP, WG_PRE_DOWN, WG_POST_DOWN } from '~/utils/config';
import {
WG_PATH,
WG_HOST,
WG_PORT,
WG_CONFIG_PORT,
WG_MTU,
WG_DEFAULT_DNS,
WG_DEFAULT_ADDRESS,
WG_PERSISTENT_KEEPALIVE,
WG_ALLOWED_IPS,
WG_PRE_UP,
WG_POST_UP,
WG_PRE_DOWN,
WG_POST_DOWN,
} from '~/utils/config';
import { exec } from '~/utils/cmd';
import { isValidIPv4 } from '~/utils/ip';
const debug = debug_logger('WireGuard')
const debug = debug_logger('WireGuard');
class ServerError extends Error {
statusCode: number;
@ -15,10 +29,9 @@ class ServerError extends Error {
super(message);
this.statusCode = statusCode;
}
};
}
class WireGuard {
async __buildConfig() {
this.__configPromise = Promise.resolve().then(async () => {
if (!WG_HOST) {
@ -62,8 +75,14 @@ class WireGuard {
await this.__saveConfig(config);
await exec('wg-quick down wg0').catch(() => {});
await exec('wg-quick up wg0').catch((err) => {
if (err && err.message && err.message.includes('Cannot find device "wg0"')) {
throw new Error('WireGuard exited with the error: Cannot find device "wg0"\nThis usually means that your host\'s kernel does not support WireGuard!');
if (
err &&
err.message &&
err.message.includes('Cannot find device "wg0"')
) {
throw new Error(
'WireGuard exited with the error: Cannot find device "wg0"\nThis usually means that your host\'s kernel does not support WireGuard!'
);
}
throw err;
@ -108,14 +127,19 @@ PostDown = ${WG_POST_DOWN}
# Client: ${client.name} (${clientId})
[Peer]
PublicKey = ${client.publicKey}
${client.preSharedKey ? `PresharedKey = ${client.preSharedKey}\n` : ''
${
client.preSharedKey ? `PresharedKey = ${client.preSharedKey}\n` : ''
}AllowedIPs = ${client.address}/32`;
}
debug('Config saving...');
await fs.writeFile(path.join(WG_PATH, 'wg0.json'), JSON.stringify(config, false, 2), {
mode: 0o660,
});
await fs.writeFile(
path.join(WG_PATH, 'wg0.json'),
JSON.stringify(config, false, 2),
{
mode: 0o660,
}
);
await fs.writeFile(path.join(WG_PATH, 'wg0.conf'), result, {
mode: 0o600,
});
@ -130,21 +154,23 @@ ${client.preSharedKey ? `PresharedKey = ${client.preSharedKey}\n` : ''
async getClients() {
const config = await this.getConfig();
const clients = Object.entries(config.clients).map(([clientId, client]) => ({
id: clientId,
name: client.name,
enabled: client.enabled,
address: client.address,
publicKey: client.publicKey,
createdAt: new Date(client.createdAt),
updatedAt: new Date(client.updatedAt),
allowedIPs: client.allowedIPs,
downloadableConfig: 'privateKey' in client,
persistentKeepalive: null,
latestHandshakeAt: null,
transferRx: null,
transferTx: null,
}));
const clients = Object.entries(config.clients).map(
([clientId, client]) => ({
id: clientId,
name: client.name,
enabled: client.enabled,
address: client.address,
publicKey: client.publicKey,
createdAt: new Date(client.createdAt),
updatedAt: new Date(client.updatedAt),
allowedIPs: client.allowedIPs,
downloadableConfig: 'privateKey' in client,
persistentKeepalive: null,
latestHandshakeAt: null,
transferRx: null,
transferTx: null,
})
);
// Loop WireGuard status
const dump = await exec('wg show wg0 dump', {
@ -157,9 +183,9 @@ ${client.preSharedKey ? `PresharedKey = ${client.preSharedKey}\n` : ''
.forEach((line) => {
const [
publicKey,
_preSharedKey,
_endpoint,
_allowedIps,
_preSharedKey,
_endpoint,
_allowedIps,
latestHandshakeAt,
transferRx,
transferTx,
@ -169,9 +195,10 @@ ${client.preSharedKey ? `PresharedKey = ${client.preSharedKey}\n` : ''
const client = clients.find((client) => client.publicKey === publicKey);
if (!client) return;
client.latestHandshakeAt = latestHandshakeAt === '0'
? null
: new Date(Number(`${latestHandshakeAt}000`));
client.latestHandshakeAt =
latestHandshakeAt === '0'
? null
: new Date(Number(`${latestHandshakeAt}000`));
client.transferRx = Number(transferRx);
client.transferTx = Number(transferTx);
client.persistentKeepalive = persistentKeepalive;
@ -203,7 +230,8 @@ ${WG_MTU ? `MTU = ${WG_MTU}\n` : ''}\
[Peer]
PublicKey = ${config.server.publicKey}
${client.preSharedKey ? `PresharedKey = ${client.preSharedKey}\n` : ''
${
client.preSharedKey ? `PresharedKey = ${client.preSharedKey}\n` : ''
}AllowedIPs = ${WG_ALLOWED_IPS}
PersistentKeepalive = ${WG_PERSISTENT_KEEPALIVE}
Endpoint = ${WG_HOST}:${WG_CONFIG_PORT}`;
@ -344,7 +372,6 @@ Endpoint = ${WG_HOST}:${WG_CONFIG_PORT}`;
async Shutdown() {
await exec('wg-quick down wg0').catch(() => {});
}
}
};
export default new WireGuard();
export default new WireGuard();

98
src/utils/api.ts

@ -1,36 +1,34 @@
export type APIClient = {
"id": string,
"name": string,
"enabled": boolean,
"address": string,
"publicKey": string,
"createdAt": string,
"updatedAt": string,
"downloadableConfig": boolean,
"persistentKeepalive": string,
"latestHandshakeAt": null,
"transferRx": number,
"transferTx": number
}
id: string;
name: string;
enabled: boolean;
address: string;
publicKey: string;
createdAt: string;
updatedAt: string;
downloadableConfig: boolean;
persistentKeepalive: string;
latestHandshakeAt: null;
transferRx: number;
transferTx: number;
};
class API {
async call({ method, path, body }: {
method: string,
path: string,
body?: Record<string, unknown>
async call({
method,
path,
body,
}: {
method: string;
path: string;
body?: Record<string, unknown>;
}) {
const res = await fetch(`./api${path}`, {
method,
headers: {
'Content-Type': 'application/json',
},
body: body
? JSON.stringify(body)
: undefined,
body: body ? JSON.stringify(body) : undefined,
});
if (res.status === 204) {
@ -81,7 +79,7 @@ class API {
});
}
async createSession({ password }: {password: string|null}) {
async createSession({ password }: { password: string | null }) {
return this.call({
method: 'post',
path: '/session',
@ -100,17 +98,20 @@ class API {
return this.call({
method: 'get',
path: '/wireguard/client',
}).then((clients: APIClient[]) => clients.map((client) => ({
...client,
createdAt: new Date(client.createdAt),
updatedAt: new Date(client.updatedAt),
latestHandshakeAt: client.latestHandshakeAt !== null
? new Date(client.latestHandshakeAt)
: null,
})));
}
async createClient({ name }: {name: string}) {
}).then((clients: APIClient[]) =>
clients.map((client) => ({
...client,
createdAt: new Date(client.createdAt),
updatedAt: new Date(client.updatedAt),
latestHandshakeAt:
client.latestHandshakeAt !== null
? new Date(client.latestHandshakeAt)
: null,
}))
);
}
async createClient({ name }: { name: string }) {
return this.call({
method: 'post',
path: '/wireguard/client',
@ -118,28 +119,34 @@ class API {
});
}
async deleteClient({ clientId }: {clientId: string}) {
async deleteClient({ clientId }: { clientId: string }) {
return this.call({
method: 'delete',
path: `/wireguard/client/${clientId}`,
});
}
async enableClient({ clientId }: {clientId: string}) {
async enableClient({ clientId }: { clientId: string }) {
return this.call({
method: 'post',
path: `/wireguard/client/${clientId}/enable`,
});
}
async disableClient({ clientId }: {clientId: string}) {
async disableClient({ clientId }: { clientId: string }) {
return this.call({
method: 'post',
path: `/wireguard/client/${clientId}/disable`,
});
}
async updateClientName({ clientId, name }: {clientId: string, name: string}) {
async updateClientName({
clientId,
name,
}: {
clientId: string;
name: string;
}) {
return this.call({
method: 'put',
path: `/wireguard/client/${clientId}/name/`,
@ -147,7 +154,13 @@ class API {
});
}
async updateClientAddress({ clientId, address }: {clientId: string, address: string}) {
async updateClientAddress({
clientId,
address,
}: {
clientId: string;
address: string;
}) {
return this.call({
method: 'put',
path: `/wireguard/client/${clientId}/address/`,
@ -162,7 +175,6 @@ class API {
body: { file },
});
}
}
export default new API();
export default new API();

39
src/utils/cmd.ts

@ -1,24 +1,29 @@
import childProcess from 'child_process';
export function exec(cmd: string, {log}: {log: boolean|string} = {log: true}) {
if (typeof log === 'string') {
console.log(`$ ${log}`);
} else if (log === true) {
console.log(`$ ${cmd}`);
}
export function exec(
cmd: string,
{ log }: { log: boolean | string } = { log: true }
) {
if (typeof log === 'string') {
console.log(`$ ${log}`);
} else if (log === true) {
console.log(`$ ${cmd}`);
}
if (process.platform !== 'linux') {
return Promise.resolve("");
}
if (process.platform !== 'linux') {
return Promise.resolve('');
}
return new Promise((resolve, reject) => {
childProcess.exec(cmd, {
return new Promise((resolve, reject) => {
childProcess.exec(
cmd,
{
shell: 'bash',
}, (err, stdout) => {
},
(err, stdout) => {
if (err) return reject(err);
return resolve(String(stdout).trim());
});
});
}
}
);
});
}

35
src/utils/config.ts

@ -1,6 +1,6 @@
import type {SessionConfig} from 'h3';
import type { SessionConfig } from 'h3';
import {getRandomHex} from '~/utils/crypto'
import { getRandomHex } from '~/utils/crypto';
import packageJSON from '../package.json';
import debug from 'debug';
const version = packageJSON.release.version;
@ -13,30 +13,41 @@ export const WG_PATH = process.env.WG_PATH || '/etc/wireguard/';
export const WG_DEVICE = process.env.WG_DEVICE || 'eth0';
export const WG_HOST = process.env.WG_HOST;
export const WG_PORT = process.env.WG_PORT || '51820';
export const WG_CONFIG_PORT = process.env.WG_CONFIG_PORT || process.env.WG_PORT || '51820';
export const WG_CONFIG_PORT =
process.env.WG_CONFIG_PORT || process.env.WG_PORT || '51820';
export const WG_MTU = process.env.WG_MTU || null;
export const WG_PERSISTENT_KEEPALIVE = process.env.WG_PERSISTENT_KEEPALIVE || '0';
export const WG_PERSISTENT_KEEPALIVE =
process.env.WG_PERSISTENT_KEEPALIVE || '0';
export const WG_DEFAULT_ADDRESS = process.env.WG_DEFAULT_ADDRESS || '10.8.0.x';
export const WG_DEFAULT_DNS = typeof process.env.WG_DEFAULT_DNS === 'string'
? process.env.WG_DEFAULT_DNS
: '1.1.1.1';
export const WG_DEFAULT_DNS =
typeof process.env.WG_DEFAULT_DNS === 'string'
? process.env.WG_DEFAULT_DNS
: '1.1.1.1';
export const WG_ALLOWED_IPS = process.env.WG_ALLOWED_IPS || '0.0.0.0/0, ::/0';
export const WG_PRE_UP = process.env.WG_PRE_UP || '';
export const WG_POST_UP = process.env.WG_POST_UP || `
export const WG_POST_UP =
process.env.WG_POST_UP ||
`
iptables -t nat -A POSTROUTING -s ${WG_DEFAULT_ADDRESS.replace('x', '0')}/24 -o ${WG_DEVICE} -j MASQUERADE;
iptables -A INPUT -p udp -m udp --dport ${WG_PORT} -j ACCEPT;
iptables -A FORWARD -i wg0 -j ACCEPT;
iptables -A FORWARD -o wg0 -j ACCEPT;
`.split('\n').join(' ');
`
.split('\n')
.join(' ');
export const WG_PRE_DOWN = process.env.WG_PRE_DOWN || '';
export const WG_POST_DOWN = process.env.WG_POST_DOWN || `
export const WG_POST_DOWN =
process.env.WG_POST_DOWN ||
`
iptables -t nat -D POSTROUTING -s ${WG_DEFAULT_ADDRESS.replace('x', '0')}/24 -o ${WG_DEVICE} -j MASQUERADE;
iptables -D INPUT -p udp -m udp --dport ${WG_PORT} -j ACCEPT;
iptables -D FORWARD -i wg0 -j ACCEPT;
iptables -D FORWARD -o wg0 -j ACCEPT;
`.split('\n').join(' ');
`
.split('\n')
.join(' ');
export const LANG = process.env.LANG || 'en';
export const UI_TRAFFIC_STATS = process.env.UI_TRAFFIC_STATS || 'false';
export const UI_CHART_TYPE = process.env.UI_CHART_TYPE || 0;
@ -44,7 +55,7 @@ export const UI_CHART_TYPE = process.env.UI_CHART_TYPE || 0;
export const REQUIRES_PASSWORD = !!PASSWORD_HASH;
export const SESSION_CONFIG = {
password: getRandomHex(256)
password: getRandomHex(256),
} satisfies SessionConfig;
export const SERVER_DEBUG = debug('Server');

10
src/utils/crypto.ts

@ -1,5 +1,7 @@
export function getRandomHex(size: number) {
const array = new Uint8Array(size);
crypto.getRandomValues(array);
return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
}
const array = new Uint8Array(size);
crypto.getRandomValues(array);
return Array.from(array, (byte) => byte.toString(16).padStart(2, '0')).join(
''
);
}

18
src/utils/ip.ts

@ -1,12 +1,12 @@
export function isValidIPv4(str) {
const blocks = str.split('.');
if (blocks.length !== 4) return false;
const blocks = str.split('.');
if (blocks.length !== 4) return false;
for (let value of blocks) {
value = parseInt(value, 10);
if (Number.isNaN(value)) return false;
if (value < 0 || value > 255) return false;
}
for (let value of blocks) {
value = parseInt(value, 10);
if (Number.isNaN(value)) return false;
if (value < 0 || value > 255) return false;
}
return true;
}
return true;
}

47
src/utils/localStorage.ts

@ -1,25 +1,30 @@
export type Theme = 'light' | 'dark' | 'auto'
export type Theme = 'light' | 'dark' | 'auto';
export type LocalStorage = {
theme: Theme,
uiShowCharts: '1' | '0',
lang: string
}
export function getItem<K extends keyof LocalStorage>(item: K): LocalStorage[K]|null {
if (import.meta.client) {
return localStorage.getItem(item) as LocalStorage[K]|null
} else {
return null
}
theme: Theme;
uiShowCharts: '1' | '0';
lang: string;
};
export function getItem<K extends keyof LocalStorage>(
item: K
): LocalStorage[K] | null {
if (import.meta.client) {
return localStorage.getItem(item) as LocalStorage[K] | null;
} else {
return null;
}
}
export function setItem<K extends keyof LocalStorage>(item: K, value: LocalStorage[K]) {
if (import.meta.client) {
localStorage.setItem(item, value)
return true
} else {
return false
}
}
export function setItem<K extends keyof LocalStorage>(
item: K,
value: LocalStorage[K]
) {
if (import.meta.client) {
localStorage.setItem(item, value);
return true;
} else {
return false;
}
}

20
src/utils/password.ts

@ -1,6 +1,6 @@
import bcrypt from 'bcryptjs';
import { PASSWORD_HASH } from "~/utils/config";
import { PASSWORD_HASH } from '~/utils/config';
/**
* Checks if `password` matches the PASSWORD_HASH.
@ -11,13 +11,13 @@ import { PASSWORD_HASH } from "~/utils/config";
* @returns {boolean} true if matching environment, otherwise false
*/
export function isPasswordValid(password: string): boolean {
if (typeof password !== 'string') {
return false;
}
if (PASSWORD_HASH) {
return bcrypt.compareSync(password, PASSWORD_HASH);
}
if (typeof password !== 'string') {
return false;
};
}
if (PASSWORD_HASH) {
return bcrypt.compareSync(password, PASSWORD_HASH);
}
return false;
}

7
src/wgpw.js

@ -6,7 +6,7 @@ const generateHash = async (password) => {
try {
const salt = await bcrypt.genSalt(12);
const hash = await bcrypt.hash(password, salt);
console.log(`PASSWORD_HASH='${hash}'`);
} catch (error) {
throw new Error(`Failed to generate hash : ${error}`);
@ -18,10 +18,8 @@ const comparePassword = async (password, hash) => {
try {
const match = await bcrypt.compare(password, hash);
if (match) {
console.log('Password matches the hash !');
} else {
console.log('Password does not match the hash.');
}
} catch (error) {
@ -44,9 +42,8 @@ const comparePassword = async (password, hash) => {
await generateHash(password);
}
} catch (error) {
console.error(error);
process.exit(1);
}
})();

Loading…
Cancel
Save