Browse Source

add prettier, format

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

6
src/.prettierrc.json

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

1323
src/app.vue

File diff suppressed because it is too large

6
src/assets/css/app.css

@ -1,6 +1,6 @@
[v-cloak] {
display: none;
}
.line-chart .apexcharts-svg{
}
.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({});

60
src/i18n.config.ts

@ -24,7 +24,8 @@ export default defineI18nConfig(() => ({
disableClient: 'Disable Client',
enableClient: 'Enable Client',
noClients: 'There are no clients yet.',
noPrivKey: 'This client has no known private key. Cannot create Configuration.',
noPrivKey:
'This client has no known private key. Cannot create Configuration.',
showQR: 'Show QR Code',
downloadConfig: 'Download Configuration',
madeBy: 'Made by',
@ -85,19 +86,25 @@ export default defineI18nConfig(() => ({
disableClient: 'Выключить клиента',
enableClient: 'Включить клиента',
noClients: 'Пока нет клиентов.',
noPrivKey: 'Невозможно создать конфигурацию: у клиента нет известного приватного ключа.',
noPrivKey:
'Невозможно создать конфигурацию: у клиента нет известного приватного ключа.',
showQR: 'Показать QR-код',
downloadConfig: 'Скачать конфигурацию',
madeBy: 'Автор',
donate: 'Поблагодарить',
toggleCharts: 'Показать/скрыть графики',
theme: { dark: 'Темная тема', light: 'Светлая тема', auto: 'Как в системе' },
theme: {
dark: 'Темная тема',
light: 'Светлая тема',
auto: 'Как в системе',
},
restore: 'Восстановить',
backup: 'Резервная копия',
titleRestoreConfig: 'Восстановить конфигурацию',
titleBackupConfig: 'Создать резервную копию конфигурации',
},
tr: { // Müslüm Barış Korkmazer @babico
tr: {
// Müslüm Barış Korkmazer @babico
name: 'İsim',
password: 'Şifre',
signIn: 'Giriş Yap',
@ -119,19 +126,25 @@ export default defineI18nConfig(() => ({
disableClient: 'Kullanıcıyı Devre Dışı Bırak',
enableClient: 'Kullanıcıyı Etkinleştir',
noClients: 'Henüz kullanıcı yok.',
noPrivKey: 'Bu istemcinin bilinen bir özel anahtarı yok. Yapılandırma oluşturulamıyor.',
noPrivKey:
'Bu istemcinin bilinen bir özel anahtarı yok. Yapılandırma oluşturulamıyor.',
showQR: 'QR Kodunu Göster',
downloadConfig: 'Yapılandırmayı İndir',
madeBy: 'Yapan Kişi: ',
donate: 'Bağış Yap',
toggleCharts: 'Grafiği göster/gizle',
theme: { dark: 'Karanlık tema', light: 'Açık tema', auto: 'Otomatik tema' },
theme: {
dark: 'Karanlık tema',
light: 'Açık tema',
auto: 'Otomatik tema',
},
restore: 'Geri yükle',
backup: 'Yedekle',
titleRestoreConfig: 'Yapılandırmanızı geri yükleyin',
titleBackupConfig: 'Yapılandırmanızı yedekleyin',
},
no: { // github.com/digvalley
no: {
// github.com/digvalley
name: 'Navn',
password: 'Passord',
signIn: 'Logg Inn',
@ -158,7 +171,8 @@ export default defineI18nConfig(() => ({
madeBy: 'Laget av',
donate: 'Doner',
},
pl: { // github.com/archont94
pl: {
// github.com/archont94
name: 'Nazwa',
password: 'Hasło',
signIn: 'Zaloguj się',
@ -185,7 +199,8 @@ export default defineI18nConfig(() => ({
madeBy: 'Stworzone przez',
donate: 'Wsparcie autora',
},
fr: { // github.com/clem3109
fr: {
// github.com/clem3109
name: 'Nom',
password: 'Mot de passe',
signIn: 'Se Connecter',
@ -216,7 +231,8 @@ export default defineI18nConfig(() => ({
titleRestoreConfig: 'Restaurer votre configuration',
titleBackupConfig: 'Sauvegarder votre configuration',
},
de: { // github.com/florian-asche
de: {
// github.com/florian-asche
name: 'Name',
password: 'Passwort',
signIn: 'Anmelden',
@ -238,7 +254,8 @@ export default defineI18nConfig(() => ({
disableClient: 'Client deaktivieren',
enableClient: 'Client aktivieren',
noClients: 'Es wurden noch keine Clients konfiguriert.',
noPrivKey: 'Es ist kein Private Key für diesen Client bekannt. Eine Konfiguration kann nicht erstellt werden.',
noPrivKey:
'Es ist kein Private Key für diesen Client bekannt. Eine Konfiguration kann nicht erstellt werden.',
showQR: 'Zeige den QR Code',
downloadConfig: 'Konfiguration herunterladen',
madeBy: 'Erstellt von',
@ -248,7 +265,8 @@ export default defineI18nConfig(() => ({
titleRestoreConfig: 'Stelle deine Konfiguration wieder her',
titleBackupConfig: 'Sichere deine Konfiguration',
},
ca: { // github.com/guillembonet
ca: {
// github.com/guillembonet
name: 'Nom',
password: 'Contrasenya',
signIn: 'Iniciar sessió',
@ -275,7 +293,8 @@ export default defineI18nConfig(() => ({
madeBy: 'Fet per',
donate: 'Donatiu',
},
es: { // github.com/amarqz
es: {
// github.com/amarqz
name: 'Nombre',
password: 'Contraseña',
signIn: 'Iniciar sesión',
@ -302,7 +321,11 @@ export default defineI18nConfig(() => ({
madeBy: 'Hecho por',
donate: 'Donar',
toggleCharts: 'Mostrar/Ocultar gráficos',
theme: { dark: 'Modo oscuro', light: 'Modo claro', auto: 'Modo automático' },
theme: {
dark: 'Modo oscuro',
light: 'Modo claro',
auto: 'Modo automático',
},
restore: 'Restaurar',
backup: 'Realizar copia de seguridad',
titleRestoreConfig: 'Restaurar su configuración',
@ -512,7 +535,7 @@ export default defineI18nConfig(() => ({
cancel: 'Annulla',
create: 'Crea',
createdOn: 'Creato il ',
lastSeen: 'Visto l\'ultima volta il ',
lastSeen: "Visto l'ultima volta il ",
totalDownload: 'Totale Download: ',
totalUpload: 'Totale Upload: ',
newClient: 'Nuovo Client',
@ -555,7 +578,8 @@ export default defineI18nConfig(() => ({
madeBy: 'สร้างโดย',
donate: 'บริจาค',
},
hi: { // github.com/rahilarious
hi: {
// github.com/rahilarious
name: 'नाम',
password: 'पासवर्ड',
signIn: 'लॉगिन',
@ -583,5 +607,5 @@ export default defineI18nConfig(() => ({
madeBy: 'सर्जक',
donate: 'दान करें',
},
}
}))
},
}));

2
src/middleware/session.ts

@ -2,4 +2,4 @@ export default defineNuxtRouteMiddleware(async (to) => {
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"
},

2
src/plugins/apexcharts.client.ts

@ -1,4 +1,4 @@
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

2
src/public/manifest.json

@ -8,4 +8,4 @@
"type": "image/png"
}
]
}
}

6
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");
assertMethod(event, 'GET');
setHeader(event, 'Content-Type', 'application/json');
return `"${LANG}"`;
})
});

6
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");
assertMethod(event, 'GET');
setHeader(event, 'Content-Type', 'application/json');
return RELEASE;
})
});

18
src/server/api/session.ts

@ -1,8 +1,12 @@
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")) {
if (isMethod(event, 'GET')) {
const session = await useSession(event, SESSION_CONFIG);
const authenticated = REQUIRES_PASSWORD
? !!(session.data && session.data.authenticated)
@ -12,7 +16,7 @@ export default defineEventHandler(async (event) => {
REQUIRES_PASSWORD,
authenticated,
};
} else if (isMethod(event, "POST")) {
} else if (isMethod(event, 'POST')) {
const session = await useSession(event, SESSION_CONFIG);
const { password } = await readBody(event);
@ -33,13 +37,13 @@ export default defineEventHandler(async (event) => {
}
const data = await session.update({
authenticated: true
authenticated: true,
});
SERVER_DEBUG(`New Session: ${data.id}`);
return { success: true };
} else if (isMethod(event, "DELETE")) {
} else if (isMethod(event, 'DELETE')) {
const session = await useSession(event, SESSION_CONFIG);
const sessionId = session.id;
@ -48,4 +52,4 @@ export default defineEventHandler(async (event) => {
SERVER_DEBUG(`Deleted Session: ${sessionId}`);
return { success: true };
}
})
});

6
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");
assertMethod(event, 'GET');
setHeader(event, 'Content-Type', 'application/json');
return `"${UI_CHART_TYPE}"`;
})
});

6
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");
assertMethod(event, 'GET');
setHeader(event, 'Content-Type', 'application/json');
return `"${UI_TRAFFIC_STATS}"`;
})
});

6
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");
assertMethod(event, 'GET');
const config = await WireGuard.backupConfiguration();
setHeader(event, 'Content-Disposition', 'attachment; filename="wg0.json"');
setHeader(event, 'Content-Type', 'text/json');
return config;
})
});

12
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");
assertMethod(event, 'PUT');
const clientId = getRouterParam(event, 'clientId');
if (clientId === '__proto__' || clientId === 'constructor' || clientId === 'prototype') {
if (
clientId === '__proto__' ||
clientId === 'constructor' ||
clientId === 'prototype'
) {
throw createError({ status: 403 });
}
const { address } = await readBody(event);
await WireGuard.updateClientAddress({ clientId, address });
return { success: true };
})
});

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

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

12
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");
assertMethod(event, 'POST');
const clientId = getRouterParam(event, 'clientId');
if (clientId === '__proto__' || clientId === 'constructor' || clientId === 'prototype') {
if (
clientId === '__proto__' ||
clientId === 'constructor' ||
clientId === 'prototype'
) {
throw createError({ status: 403 });
}
await WireGuard.disableClient({ clientId });
return { success: true };
})
});

12
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");
assertMethod(event, 'POST');
const clientId = getRouterParam(event, 'clientId');
if (clientId === '__proto__' || clientId === 'constructor' || clientId === 'prototype') {
if (
clientId === '__proto__' ||
clientId === 'constructor' ||
clientId === 'prototype'
) {
throw createError({ status: 403 });
}
await WireGuard.enableClient({ clientId });
return { success: true };
})
});

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

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

12
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");
assertMethod(event, 'PUT');
const clientId = getRouterParam(event, 'clientId');
if (clientId === '__proto__' || clientId === 'constructor' || clientId === 'prototype') {
if (
clientId === '__proto__' ||
clientId === 'constructor' ||
clientId === 'prototype'
) {
throw createError({ status: 403 });
}
const { name } = await readBody(event);
await WireGuard.updateClientName({ clientId, name });
return { success: true };
})
});

6
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");
assertMethod(event, 'GET');
const clientId = getRouterParam(event, 'clientId');
const svg = await WireGuard.getClientQRCodeSVG({ clientId });
setHeader(event, 'Content-Type', 'image/svg+xml');
return svg;
})
});

8
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")) {
if (isMethod(event, 'GET')) {
return WireGuard.getClients();
} else if (isMethod(event, "POST")) {
} else if (isMethod(event, 'POST')) {
const { name } = await readBody(event);
await WireGuard.createClient({ name });
return { success: true };
}
})
});

6
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");
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;

59
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), {
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,7 +154,8 @@ ${client.preSharedKey ? `PresharedKey = ${client.preSharedKey}\n` : ''
async getClients() {
const config = await this.getConfig();
const clients = Object.entries(config.clients).map(([clientId, client]) => ({
const clients = Object.entries(config.clients).map(
([clientId, client]) => ({
id: clientId,
name: client.name,
enabled: client.enabled,
@ -144,7 +169,8 @@ ${client.preSharedKey ? `PresharedKey = ${client.preSharedKey}\n` : ''
latestHandshakeAt: null,
transferRx: null,
transferTx: null,
}));
})
);
// Loop WireGuard status
const dump = await exec('wg show wg0 dump', {
@ -169,7 +195,8 @@ ${client.preSharedKey ? `PresharedKey = ${client.preSharedKey}\n` : ''
const client = clients.find((client) => client.publicKey === publicKey);
if (!client) return;
client.latestHandshakeAt = latestHandshakeAt === '0'
client.latestHandshakeAt =
latestHandshakeAt === '0'
? null
: new Date(Number(`${latestHandshakeAt}000`));
client.transferRx = Number(transferRx);
@ -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();

82
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) => ({
}).then((clients: APIClient[]) =>
clients.map((client) => ({
...client,
createdAt: new Date(client.createdAt),
updatedAt: new Date(client.updatedAt),
latestHandshakeAt: client.latestHandshakeAt !== null
latestHandshakeAt:
client.latestHandshakeAt !== null
? new Date(client.latestHandshakeAt)
: null,
})));
}))
);
}
async createClient({ name }: {name: string}) {
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();

21
src/utils/cmd.ts

@ -1,24 +1,29 @@
import childProcess from 'child_process';
export function exec(cmd: string, {log}: {log: boolean|string} = {log: true}) {
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("");
return Promise.resolve('');
}
return new Promise((resolve, reject) => {
childProcess.exec(cmd, {
childProcess.exec(
cmd,
{
shell: 'bash',
}, (err, stdout) => {
},
(err, stdout) => {
if (err) return reject(err);
return resolve(String(stdout).trim());
});
});
}
);
});
}

31
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'
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');

4
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('');
return Array.from(array, (byte) => byte.toString(16).padStart(2, '0')).join(
''
);
}

2
src/utils/ip.ts

@ -9,4 +9,4 @@ export function isValidIPv4(str) {
}
return true;
}
}

37
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
}
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 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]) {
export function setItem<K extends keyof LocalStorage>(
item: K,
value: LocalStorage[K]
) {
if (import.meta.client) {
localStorage.setItem(item, value)
localStorage.setItem(item, value);
return true
return true;
} else {
return false
}
return false;
}
}

4
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.
@ -20,4 +20,4 @@ export function isPasswordValid(password: string): boolean {
}
return false;
};
}

3
src/wgpw.js

@ -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,7 +42,6 @@ const comparePassword = async (password, hash) => {
await generateHash(password);
}
} catch (error) {
console.error(error);
process.exit(1);

Loading…
Cancel
Save