diff --git a/src/nuxt.config.ts b/src/nuxt.config.ts index 54ef6923..57b2cf54 100644 --- a/src/nuxt.config.ts +++ b/src/nuxt.config.ts @@ -50,4 +50,8 @@ export default defineNuxtConfig({ '#db': fileURLToPath(new URL('./server/database/', import.meta.url)), }, }, + alias: { + // for typecheck reasons (https://github.com/nuxt/cli/issues/323) + '#db': fileURLToPath(new URL('./server/database/', import.meta.url)), + }, }); diff --git a/src/server/database/migrations/0000_fantastic_zemo.sql b/src/server/database/migrations/0000_faulty_plazm.sql similarity index 98% rename from src/server/database/migrations/0000_fantastic_zemo.sql rename to src/server/database/migrations/0000_faulty_plazm.sql index a7185beb..5d0c44d9 100644 --- a/src/server/database/migrations/0000_fantastic_zemo.sql +++ b/src/server/database/migrations/0000_faulty_plazm.sql @@ -79,7 +79,7 @@ CREATE TABLE `users_table` ( `email` text, `name` text NOT NULL, `role` integer NOT NULL, - `enabled` integer DEFAULT 1 NOT NULL, + `enabled` integer NOT NULL, `created_at` text DEFAULT (CURRENT_TIMESTAMP) NOT NULL, `updated_at` text DEFAULT (CURRENT_TIMESTAMP) NOT NULL ); diff --git a/src/server/database/migrations/0001_lonely_tusk.sql b/src/server/database/migrations/0001_lonely_tusk.sql deleted file mode 100644 index 6fc9a5df..00000000 --- a/src/server/database/migrations/0001_lonely_tusk.sql +++ /dev/null @@ -1,3 +0,0 @@ --- Custom SQL migration file, put your code below! -- -INSERT INTO `general_table` (`setupStep`, `session_password`, `session_timeout`) -VALUES (1, hex(randomblob(256)), 3600); diff --git a/src/server/database/migrations/0001_next_george_stacy.sql b/src/server/database/migrations/0001_next_george_stacy.sql new file mode 100644 index 00000000..481f4fb1 --- /dev/null +++ b/src/server/database/migrations/0001_next_george_stacy.sql @@ -0,0 +1,15 @@ +-- Insert default values -- +INSERT INTO `general_table` (`setupStep`, `session_password`, `session_timeout`) +VALUES (1, hex(randomblob(256)), 3600); + +INSERT INTO `interfaces_table` (`name`, `device`, `port`, `private_key`, `public_key`, `ipv4_cidr`, `ipv6_cidr`, `mtu`, `enabled`) +VALUES ('wg0', 'eth0', 51820, '---default---', '---default---', '10.8.0.0/24', 'fdcc:ad94:bacf:61a4::cafe:0/112', 1420, 1); + +INSERT INTO `hooks_table` (`id`, `pre_up`, `post_up`, `pre_down`, `post_down`) +VALUES ( + 'wg0', + '', + 'iptables -t nat -A POSTROUTING -s {{ipv4Cidr}} -o {{device}} -j MASQUERADE; iptables -A INPUT -p udp -m udp --dport {{port}} -j ACCEPT; iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; ip6tables -t nat -A POSTROUTING -s {{ipv6Cidr}} -o {{device}} -j MASQUERADE; ip6tables -A INPUT -p udp -m udp --dport {{port}} -j ACCEPT; ip6tables -A FORWARD -i wg0 -j ACCEPT; ip6tables -A FORWARD -o wg0 -j ACCEPT;', + '', + 'iptables -t nat -D POSTROUTING -s {{ipv4Cidr}} -o {{device}} -j MASQUERADE; iptables -D INPUT -p udp -m udp --dport {{port}} -j ACCEPT; iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; ip6tables -t nat -D POSTROUTING -s {{ipv6Cidr}} -o {{device}} -j MASQUERADE; ip6tables -D INPUT -p udp -m udp --dport {{port}} -j ACCEPT; ip6tables -D FORWARD -i wg0 -j ACCEPT; ip6tables -D FORWARD -o wg0 -j ACCEPT;' +); diff --git a/src/server/database/migrations/meta/0000_snapshot.json b/src/server/database/migrations/meta/0000_snapshot.json index 6a4d7584..68d31ed1 100644 --- a/src/server/database/migrations/meta/0000_snapshot.json +++ b/src/server/database/migrations/meta/0000_snapshot.json @@ -1,7 +1,7 @@ { "version": "6", "dialect": "sqlite", - "id": "52812a10-9028-40dc-b1dc-69e4e641aa9e", + "id": "37203c54-1625-40bd-89fd-9be0f7140fae", "prevId": "00000000-0000-0000-0000-000000000000", "tables": { "clients_table": { @@ -542,8 +542,7 @@ "type": "integer", "primaryKey": false, "notNull": true, - "autoincrement": false, - "default": 1 + "autoincrement": false }, "created_at": { "name": "created_at", diff --git a/src/server/database/migrations/meta/0001_snapshot.json b/src/server/database/migrations/meta/0001_snapshot.json index 5a5def77..443bacdd 100644 --- a/src/server/database/migrations/meta/0001_snapshot.json +++ b/src/server/database/migrations/meta/0001_snapshot.json @@ -1,6 +1,6 @@ { - "id": "5eee8e4e-69d5-4c68-b5cd-a221ad7649de", - "prevId": "52812a10-9028-40dc-b1dc-69e4e641aa9e", + "id": "1bc0abb2-1297-42d2-ba8e-213bbfd92835", + "prevId": "37203c54-1625-40bd-89fd-9be0f7140fae", "version": "6", "dialect": "sqlite", "tables": { @@ -542,8 +542,7 @@ "type": "integer", "primaryKey": false, "notNull": true, - "autoincrement": false, - "default": 1 + "autoincrement": false }, "created_at": { "name": "created_at", diff --git a/src/server/database/migrations/meta/_journal.json b/src/server/database/migrations/meta/_journal.json index 82904dfc..f43835c2 100644 --- a/src/server/database/migrations/meta/_journal.json +++ b/src/server/database/migrations/meta/_journal.json @@ -5,15 +5,15 @@ { "idx": 0, "version": "6", - "when": 1737101897688, - "tag": "0000_fantastic_zemo", + "when": 1737107311477, + "tag": "0000_faulty_plazm", "breakpoints": true }, { "idx": 1, "version": "6", - "when": 1737101904944, - "tag": "0001_lonely_tusk", + "when": 1737107315085, + "tag": "0001_next_george_stacy", "breakpoints": true } ] diff --git a/src/server/database/repositories/client/service.ts b/src/server/database/repositories/client/service.ts index 69c17204..f4d31776 100644 --- a/src/server/database/repositories/client/service.ts +++ b/src/server/database/repositories/client/service.ts @@ -2,6 +2,7 @@ import type { DBType } from '#db/sqlite'; import { eq, sql } from 'drizzle-orm'; import { client } from './schema'; import type { ClientCreateType } from './types'; +import type { ID } from '../../schema'; import { wgInterface, userConfig } from '../../schema'; import { parseCidr } from 'cidr-tools'; @@ -17,6 +18,11 @@ function createPreparedStatement(db: DBType) { findById: db.query.client .findFirst({ where: eq(client.id, sql.placeholder('id')) }) .prepare(), + toggle: db + .update(client) + .set({ enabled: sql.placeholder('enabled') as never as boolean }) + .where(eq(client.id, sql.placeholder('id'))) + .prepare(), }; } @@ -38,7 +44,7 @@ export class ClientService { })); } - async get(id: number) { + async get(id: ID) { return this.#statements.findById.execute({ id }); } @@ -103,4 +109,8 @@ export class ClientService { .execute(); }); } + + async toggle(id: ID, enabled: boolean) { + return this.#statements.toggle.execute({ id, enabled }); + } } diff --git a/src/server/database/repositories/hooks/service.ts b/src/server/database/repositories/hooks/service.ts new file mode 100644 index 00000000..1384070a --- /dev/null +++ b/src/server/database/repositories/hooks/service.ts @@ -0,0 +1,23 @@ +import type { DBType } from '#db/sqlite'; +import { eq, sql } from 'drizzle-orm'; +import { hooks } from './schema'; + +function createPreparedStatement(db: DBType) { + return { + get: db.query.hooks + .findFirst({ where: eq(hooks.id, sql.placeholder('interface')) }) + .prepare(), + }; +} + +export class HooksService { + #statements: ReturnType; + + constructor(db: DBType) { + this.#statements = createPreparedStatement(db); + } + + get(wgInterface: string) { + return this.#statements.get.execute({ interface: wgInterface }); + } +} diff --git a/src/server/database/repositories/interface/service.ts b/src/server/database/repositories/interface/service.ts new file mode 100644 index 00000000..7fa3e313 --- /dev/null +++ b/src/server/database/repositories/interface/service.ts @@ -0,0 +1,44 @@ +import type { DBType } from '#db/sqlite'; +import { eq, sql } from 'drizzle-orm'; +import { wgInterface } from './schema'; + +function createPreparedStatement(db: DBType) { + return { + get: db.query.wgInterface + .findFirst({ where: eq(wgInterface.name, sql.placeholder('interface')) }) + .prepare(), + getAll: db.query.wgInterface.findMany().prepare(), + updateKeyPair: db + .update(wgInterface) + .set({ + privateKey: sql.placeholder('privateKey') as never as string, + publicKey: sql.placeholder('publicKey') as never as string, + }) + .where(eq(wgInterface.name, sql.placeholder('interface'))) + .prepare(), + }; +} + +export class InterfaceService { + #statements: ReturnType; + + constructor(db: DBType) { + this.#statements = createPreparedStatement(db); + } + + get(infName: string) { + return this.#statements.get.execute({ interface: infName }); + } + + getAll() { + return this.#statements.getAll.execute(); + } + + updateKeyPair(infName: string, privateKey: string, publicKey: string) { + return this.#statements.updateKeyPair.execute({ + interface: infName, + privateKey, + publicKey, + }); + } +} diff --git a/src/server/database/repositories/oneTimeLink/service.ts b/src/server/database/repositories/oneTimeLink/service.ts new file mode 100644 index 00000000..8d1ec5b3 --- /dev/null +++ b/src/server/database/repositories/oneTimeLink/service.ts @@ -0,0 +1,25 @@ +import type { DBType } from '#db/sqlite'; +import { eq, sql } from 'drizzle-orm'; +import { oneTimeLink } from './schema'; +import type { ID } from '../../schema'; + +function createPreparedStatement(db: DBType) { + return { + delete: db + .delete(oneTimeLink) + .where(eq(oneTimeLink.id, sql.placeholder('id'))) + .prepare(), + }; +} + +export class OneTimeLinkService { + #statements: ReturnType; + + constructor(db: DBType) { + this.#statements = createPreparedStatement(db); + } + + delete(id: ID) { + return this.#statements.delete.execute({ id }); + } +} diff --git a/src/server/database/repositories/userConfig/service.ts b/src/server/database/repositories/userConfig/service.ts index fe4517da..bbf9e5ec 100644 --- a/src/server/database/repositories/userConfig/service.ts +++ b/src/server/database/repositories/userConfig/service.ts @@ -4,6 +4,9 @@ import { userConfig } from './schema'; function createPreparedStatement(db: DBType) { return { + get: db.query.userConfig + .findFirst({ where: eq(userConfig.id, sql.placeholder('interface')) }) + .prepare(), updateHostPort: db .update(userConfig) .set({ @@ -22,6 +25,10 @@ export class UserConfigService { this.#statements = createPreparedStatement(db); } + async get(wgInterface: string) { + return await this.#statements.get.execute({ interface: wgInterface }); + } + async updateHostPort(wgInterface: string, host: string, port: number) { return await this.#statements.updateHostPort.execute({ interface: wgInterface, diff --git a/src/server/database/sqlite.ts b/src/server/database/sqlite.ts index 0f44af76..b02fa59f 100644 --- a/src/server/database/sqlite.ts +++ b/src/server/database/sqlite.ts @@ -7,6 +7,9 @@ import { ClientService } from './repositories/client/service'; import { GeneralService } from './repositories/general/service'; import { UserService } from './repositories/user/service'; import { UserConfigService } from './repositories/userConfig/service'; +import { InterfaceService } from './repositories/interface/service'; +import { HooksService } from './repositories/hooks/service'; +import { OneTimeLinkService } from './repositories/oneTimeLink/service'; const client = createClient({ url: 'file:/etc/wireguard/wg0.db' }); const db = drizzle({ client, schema }); @@ -21,11 +24,17 @@ class DBService { general: GeneralService; users: UserService; userConfigs: UserConfigService; + interfaces: InterfaceService; + hooks: HooksService; + oneTimeLinks: OneTimeLinkService; constructor(db: DBType) { this.clients = new ClientService(db); this.general = new GeneralService(db); this.users = new UserService(db); this.userConfigs = new UserConfigService(db); + this.interfaces = new InterfaceService(db); + this.hooks = new HooksService(db); + this.oneTimeLinks = new OneTimeLinkService(db); } } @@ -38,7 +47,6 @@ async function migrate() { await drizzleMigrate(db, { migrationsFolder: './server/database/migrations', }); - // TODO: data migration console.log('Migration complete'); } catch (e) { if (e instanceof Error) { diff --git a/src/server/utils/Database.ts b/src/server/utils/Database.ts index 8550dafa..297eba2c 100644 --- a/src/server/utils/Database.ts +++ b/src/server/utils/Database.ts @@ -18,7 +18,7 @@ let provider = nullObject as never as DBServiceType; connect().then((db) => { provider = db; - // TODO: start wireguard + WireGuard.Startup(); }); // TODO: check if old config exists and tell user about migration path diff --git a/src/server/utils/WireGuard.ts b/src/server/utils/WireGuard.ts index 892138ad..06e55184 100644 --- a/src/server/utils/WireGuard.ts +++ b/src/server/utils/WireGuard.ts @@ -1,10 +1,7 @@ import fs from 'node:fs/promises'; import debug from 'debug'; import QRCode from 'qrcode'; -import CRC32 from 'crc-32'; -import isCidr from 'is-cidr'; - -import type { UpdateClient } from '~~/services/database/repositories/client'; +import type { ID } from '#db/schema'; const DEBUG = debug('WireGuard'); @@ -13,20 +10,26 @@ class WireGuard { * Save and sync config */ async saveConfig() { - await this.#saveWireguardConfig(); - await this.#syncWireguardConfig(); + await this.#saveWireguardConfig('wg0'); + await this.#syncWireguardConfig('wg0'); } /** * Generates and saves WireGuard config from database as wg0 */ - async #saveWireguardConfig() { - const system = await Database.get(); - const clients = await Database.client.findAll(); + async #saveWireguardConfig(infName: string) { + const wgInterface = await Database.interfaces.get(infName); + const clients = await Database.clients.getAll(); + const hooks = await Database.hooks.get(infName); + + if (!wgInterface || !hooks) { + throw new Error('Interface or Hooks not found'); + } + const result = []; - result.push(wg.generateServerInterface(system)); + result.push(wg.generateServerInterface(wgInterface, hooks)); - for (const client of Object.values(clients)) { + for (const client of clients) { if (!client.enabled) { continue; } @@ -34,15 +37,15 @@ class WireGuard { } DEBUG('Saving Config...'); - await fs.writeFile('/etc/wireguard/wg0.conf', result.join('\n\n'), { + await fs.writeFile(`/etc/wireguard/${infName}.conf`, result.join('\n\n'), { mode: 0o600, }); DEBUG('Config saved successfully.'); } - async #syncWireguardConfig() { + async #syncWireguardConfig(infName: string) { DEBUG('Syncing Config...'); - await wg.sync(); + await wg.sync(infName); DEBUG('Config synced successfully.'); } @@ -57,7 +60,7 @@ class WireGuard { })); // Loop WireGuard status - const dump = await wg.dump(); + const dump = await wg.dump('wg0'); dump.forEach( ({ publicKey, latestHandshakeAt, endpoint, transferRx, transferTx }) => { const client = clients.find((client) => client.publicKey === publicKey); @@ -75,14 +78,24 @@ class WireGuard { return clients; } - async getClientConfiguration({ clientId }: { clientId: number }) { - const system = await Database.system.get(); + async getClientConfiguration({ clientId }: { clientId: ID }) { + const wgInterface = await Database.interfaces.get('wg0'); + const userConfig = await Database.userConfigs.get('wg0'); + + if (!wgInterface || !userConfig) { + throw new Error('Interface or UserConfig not found'); + } + const client = await Database.clients.get(clientId); - return wg.generateClientConfig(system, client); + if (!client) { + throw new Error('Client not found'); + } + + return wg.generateClientConfig(wgInterface, userConfig, client); } - async getClientQRCodeSVG({ clientId }: { clientId: string }) { + async getClientQRCodeSVG({ clientId }: { clientId: ID }) { const config = await this.getClientConfiguration({ clientId }); return QRCode.toString(config, { type: 'svg', @@ -90,85 +103,6 @@ class WireGuard { }); } - async deleteClient({ clientId }: { clientId: string }) { - await Database.client.delete(clientId); - await this.saveConfig(); - } - - async enableClient({ clientId }: { clientId: string }) { - await Database.client.toggle(clientId, true); - - await this.saveConfig(); - } - - async generateOneTimeLink({ clientId }: { clientId: string }) { - const key = `${clientId}-${Math.floor(Math.random() * 1000)}`; - const oneTimeLink = Math.abs(CRC32.str(key)).toString(16); - const expiresAt = new Date(Date.now() + 5 * 60 * 1000).toISOString(); - await Database.client.createOneTimeLink(clientId, { - oneTimeLink, - expiresAt, - }); - await this.saveConfig(); - } - - async eraseOneTimeLink({ clientId }: { clientId: string }) { - await Database.client.deleteOneTimeLink(clientId); - await this.saveConfig(); - } - - async disableClient({ clientId }: { clientId: string }) { - await Database.client.toggle(clientId, false); - - await this.saveConfig(); - } - - async updateClient({ - clientId, - client, - }: { - clientId: string; - client: UpdateClient; - }) { - // TODO: validate ipv4, v6, expire date etc - await Database.client.update(clientId, client); - await this.saveConfig(); - } - - async updateAddressRange({ - address4, - address6, - }: { - address4: string; - address6: string; - }) { - // TODO: be able to revert if error - - if (!isCidr(address4) || !isCidr(address6)) { - throw new Error('Invalid CIDR'); - } - - await Database.system.updateAddressRange(address4, address6); - - const systems = await Database.system.get(); - const clients = await Database.client.findAll(); - - for (const _client of Object.values(clients)) { - const clients = await Database.client.findAll(); - - const client = structuredClone(_client) as DeepWriteable; - - client.address4 = nextIPv4(systems, clients); - client.address6 = nextIPv6(systems, clients); - - await Database.client.update(client.id, { - ...client, - }); - } - - await this.saveConfig(); - } - // TODO: reimplement database restore async restoreConfiguration(_config: string) { /* DEBUG('Starting configuration restore process.'); @@ -189,24 +123,47 @@ class WireGuard { } async Startup() { - DEBUG('Starting Wireguard...'); - await this.#saveWireguardConfig(); - await wg.down().catch(() => {}); - await wg.up().catch((err) => { + const wgInterfaces = await Database.interfaces.getAll(); + for (const wgInterface of wgInterfaces) { + if (wgInterface.enabled !== true) { + continue; + } + // default interface has no keys if ( - err && - err.message && - err.message.includes('Cannot find device "wg0"') + wgInterface.privateKey === '---default---' && + wgInterface.publicKey === '---default---' ) { - throw new Error( - 'WireGuard exited with the error: Cannot find device "wg0"\nThis usually means that your host\'s kernel does not support WireGuard!' + DEBUG('Generating new Wireguard Keys...'); + const privateKey = await wg.generatePrivateKey(); + const publicKey = await wg.getPublicKey(privateKey); + + await Database.interfaces.updateKeyPair( + wgInterface.name, + privateKey, + publicKey ); + DEBUG('New Wireguard Keys generated successfully.'); } + DEBUG(`Starting Wireguard Interface ${wgInterface.name}...`); + await this.#saveWireguardConfig(wgInterface.name); + await wg.down(wgInterface.name).catch(() => {}); + await wg.up(wgInterface.name).catch((err) => { + if ( + err && + err.message && + err.message.includes(`Cannot find device "${wgInterface.name}"`) + ) { + throw new Error( + `WireGuard exited with the error: Cannot find device "${wgInterface.name}"\nThis usually means that your host's kernel does not support WireGuard!`, + { cause: err.message } + ); + } - throw err; - }); - await this.#syncWireguardConfig(); - DEBUG('Wireguard started successfully.'); + throw err; + }); + await this.#syncWireguardConfig(wgInterface.name); + DEBUG(`Wireguard Interface ${wgInterface.name} started successfully.`); + } DEBUG('Starting Cron Job.'); await this.startCronJob(); @@ -214,7 +171,6 @@ class WireGuard { } // TODO: handle as worker_thread - // would need a better database aswell async startCronJob() { await this.cronJob().catch((err) => { DEBUG('Running Cron Job failed.'); @@ -227,31 +183,34 @@ class WireGuard { // Shutdown wireguard async Shutdown() { - await wg.down().catch(() => {}); + const wgInterfaces = await Database.interfaces.getAll(); + for (const wgInterface of wgInterfaces) { + await wg.down(wgInterface.name).catch(() => {}); + } } async cronJob() { - const clients = await Database.client.findAll(); + const clients = await Database.clients.getAll(); // Expires Feature - for (const client of Object.values(clients)) { + for (const client of clients) { if (client.enabled !== true) continue; if ( client.expiresAt !== null && new Date() > new Date(client.expiresAt) ) { DEBUG(`Client ${client.id} expired.`); - await Database.client.toggle(client.id, false); + await Database.clients.toggle(client.id, false); } } // One Time Link Feature - for (const client of Object.values(clients)) { + for (const client of clients) { if ( client.oneTimeLink !== null && new Date() > new Date(client.oneTimeLink.expiresAt) ) { DEBUG(`Client ${client.id} One Time Link expired.`); - await Database.client.deleteOneTimeLink(client.id); + await Database.oneTimeLinks.delete(client.id); } } @@ -266,7 +225,7 @@ class WireGuard { let wireguardSentBytes = ''; let wireguardReceivedBytes = ''; let wireguardLatestHandshakeSeconds = ''; - for (const client of Object.values(clients)) { + for (const client of clients) { wireguardPeerCount++; if (client.enabled === true) { wireguardEnabledPeersCount++; @@ -274,9 +233,9 @@ class WireGuard { if (client.endpoint !== null) { wireguardConnectedPeersCount++; } - wireguardSentBytes += `wireguard_sent_bytes{interface="wg0",enabled="${client.enabled}",address4="${client.address4}",address6="${client.address6}",name="${client.name}"} ${Number(client.transferTx)}\n`; - wireguardReceivedBytes += `wireguard_received_bytes{interface="wg0",enabled="${client.enabled}",address4="${client.address4}",address6="${client.address6}",name="${client.name}"} ${Number(client.transferRx)}\n`; - wireguardLatestHandshakeSeconds += `wireguard_latest_handshake_seconds{interface="wg0",enabled="${client.enabled}",address4="${client.address4}",address6="${client.address6}",name="${client.name}"} ${client.latestHandshakeAt ? (new Date().getTime() - new Date(client.latestHandshakeAt).getTime()) / 1000 : 0}\n`; + wireguardSentBytes += `wireguard_sent_bytes{interface="wg0",enabled="${client.enabled}",ipv4Address="${client.ipv4Address}",ipv6Address="${client.ipv6Address}",name="${client.name}"} ${Number(client.transferTx)}\n`; + wireguardReceivedBytes += `wireguard_received_bytes{interface="wg0",enabled="${client.enabled}",ipv4Address="${client.ipv4Address}",ipv6Address="${client.ipv6Address}",name="${client.name}"} ${Number(client.transferRx)}\n`; + wireguardLatestHandshakeSeconds += `wireguard_latest_handshake_seconds{interface="wg0",enabled="${client.enabled}",ipv4Address="${client.ipv4Address}",ipv6Address="${client.ipv6Address}",name="${client.name}"} ${client.latestHandshakeAt ? (new Date().getTime() - new Date(client.latestHandshakeAt).getTime()) / 1000 : 0}\n`; } let returnText = '# HELP wg-easy and wireguard metrics\n'; @@ -315,7 +274,7 @@ class WireGuard { let wireguardPeerCount = 0; let wireguardEnabledPeersCount = 0; let wireguardConnectedPeersCount = 0; - for (const client of Object.values(clients)) { + for (const client of clients) { wireguardPeerCount++; if (client.enabled === true) { wireguardEnabledPeersCount++; diff --git a/src/server/utils/ip.ts b/src/server/utils/ip.ts index 26fd33cc..0a5bb3dc 100644 --- a/src/server/utils/ip.ts +++ b/src/server/utils/ip.ts @@ -13,7 +13,7 @@ export function nextIP( let address; for (let i = cidr.start + 2n; i <= cidr.end - 1n; i++) { const currentIp = stringifyIp({ number: i, version: version }); - const client = Object.values(clients).find((client) => { + const client = clients.find((client) => { return client[`ipv${version}Address`] === currentIp; }); diff --git a/src/server/utils/wgHelper.ts b/src/server/utils/wgHelper.ts index e7d52a30..42ac16af 100644 --- a/src/server/utils/wgHelper.ts +++ b/src/server/utils/wgHelper.ts @@ -5,10 +5,8 @@ import { stringifyIp } from 'ip-bigint'; import type { UserConfigType } from '#db/repositories/userConfig/types'; import type { HooksType } from '#db/repositories/hooks/types'; -// TODO: replace wg0 with parameter (to allow multi interface design) - export const wg = { - generateServerPeer: (client: ClientType) => { + generateServerPeer: (client: Omit) => { const allowedIps = [ `${client.ipv4Address}/32`, `${client.ipv6Address}/128`, @@ -79,20 +77,20 @@ Endpoint = ${userConfig.host}:${userConfig.port}`; return exec('wg genpsk'); }, - up: () => { - return exec('wg-quick up wg0'); + up: (infName: string) => { + return exec(`wg-quick up ${infName}`); }, - down: () => { - return exec('wg-quick down wg0'); + down: (infName: string) => { + return exec(`wg-quick down ${infName}`); }, - sync: () => { - return exec('wg syncconf wg0 <(wg-quick strip wg0)'); + sync: (infName: string) => { + return exec(`wg syncconf ${infName} <(wg-quick strip ${infName})`); }, - dump: async () => { - const rawDump = await exec('wg show wg0 dump', { + dump: async (infName: string) => { + const rawDump = await exec(`wg show ${infName} dump`, { log: false, });