diff --git a/Dockerfile b/Dockerfile index b6c55aa..6745e4c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,11 +15,11 @@ RUN apk update && apk upgrade \ iproute2 iptables net-tools \ screen curl bash \ wireguard-tools \ - openssl \ - tor \ - redis \ + tor &&\ + # NPM packages + npm install -g @litehex/node-checksum &&\ # Clear APK cache - && rm -rf /var/cache/apk/* + rm -rf /var/cache/apk/* COPY /config/torrc /etc/tor/torrc diff --git a/Dockerfile-Dev b/Dockerfile-Dev index 3f09fc5..c347cd4 100644 --- a/Dockerfile-Dev +++ b/Dockerfile-Dev @@ -15,11 +15,11 @@ RUN apk update && apk upgrade \ iproute2 iptables net-tools \ screen vim curl bash \ wireguard-tools \ - openssl \ - tor \ - redis \ + tor &&\ + # NPM packages + npm install -g @litehex/node-checksum &&\ # Clear APK cache - && rm -rf /var/cache/apk/* + rm -rf /var/cache/apk/* COPY /config/torrc /etc/tor/torrc COPY /config/obfs4-bridges.conf /etc/torrc.d/obfs4-bridges.conf @@ -34,6 +34,9 @@ ENV PNPM_HOME="/pnpm" ENV PATH="$PNPM_HOME:$PATH" RUN corepack enable +ENV NODE_ENV=development +ENV LOG_LEVEL=debug + COPY docker-entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"] diff --git a/web/package.json b/web/package.json index b9a95b5..f438d6c 100644 --- a/web/package.json +++ b/web/package.json @@ -45,13 +45,13 @@ "zod": "^3.22.4" }, "dependencies": { + "@litehex/storage-box": "^0.1.1-canary.0", "bits-ui": "^0.17.0", "clsx": "^2.1.0", "deepmerge": "^4.3.1", "dotenv": "^16.4.1", "execa": "^8.0.1", "formsnap": "^0.4.3", - "ioredis": "^5.3.2", "jsonwebtoken": "^9.0.2", "lucide-svelte": "^0.323.0", "node-netkit": "0.1.0-canary.1", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index bf06f9f..9ae6027 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -5,6 +5,9 @@ settings: excludeLinksFromLockfile: false dependencies: + '@litehex/storage-box': + specifier: ^0.1.1-canary.0 + version: 0.1.1-canary.0 bits-ui: specifier: ^0.17.0 version: 0.17.0(svelte@4.2.10) @@ -23,9 +26,6 @@ dependencies: formsnap: specifier: ^0.4.3 version: 0.4.3(svelte@4.2.10)(sveltekit-superforms@1.13.4)(zod@3.22.4) - ioredis: - specifier: ^5.3.2 - version: 5.3.2 jsonwebtoken: specifier: ^9.0.2 version: 9.0.2 @@ -361,10 +361,6 @@ packages: '@swc/helpers': 0.5.3 dev: false - /@ioredis/commands@1.2.0: - resolution: {integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==} - dev: false - /@jridgewell/gen-mapping@0.3.3: resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} engines: {node: '>=6.0.0'} @@ -390,6 +386,14 @@ packages: '@jridgewell/resolve-uri': 3.1.1 '@jridgewell/sourcemap-codec': 1.4.15 + /@litehex/storage-box@0.1.1-canary.0: + resolution: { integrity: sha512-wUSYhr8jdbrBDeKu0HHmaXVLKAJWXtyD41g/6deCROQ0OwyQwNPCOWwAjMVzdrIpTo6fyGl+QliMxyNSUqbCqw== } + dependencies: + '@msgpack/msgpack': 3.0.0-beta2 + debounce: 2.0.0 + type-fest: 4.10.2 + dev: false + /@melt-ui/svelte@0.71.2(svelte@4.2.10): resolution: { integrity: sha512-GDUErhAphEoEOLpcBjQ84BgzRR6M3344fQE4QYFffwT7aedWak7CvNsECgeig1Y5xvfDmeEaFnGlOQXIBucJYw== } peerDependencies: @@ -404,6 +408,11 @@ packages: svelte: 4.2.10 dev: false + /@msgpack/msgpack@3.0.0-beta2: + resolution: { integrity: sha512-y+l1PNV0XDyY8sM3YtuMLK5vE3/hkfId+Do8pLo/OPxfxuFAUwcGz3oiiUuV46/aBpwTzZ+mRWVMtlSKbradhw== } + engines: { node: '>= 14' } + dev: false + /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -1042,11 +1051,6 @@ packages: engines: {node: '>=6'} dev: false - /cluster-key-slot@1.1.2: - resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} - engines: {node: '>=0.10.0'} - dev: false - /code-red@1.0.4: resolution: {integrity: sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==} dependencies: @@ -1109,6 +1113,11 @@ packages: resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} dev: false + /debounce@2.0.0: + resolution: { integrity: sha512-xRetU6gL1VJbs85Mc4FoEGSjQxzpdxRyFhe3lmWFyy2EzydIcD4xzUvRJMD+NPDfMwKNhxa3PvsIOU32luIWeA== } + engines: { node: '>=18' } + dev: false + /debug@4.3.4(supports-color@8.1.1): resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -1140,11 +1149,6 @@ packages: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} - /denque@2.1.0: - resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} - engines: {node: '>=0.10'} - dev: false - /dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -1503,23 +1507,6 @@ packages: /inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - /ioredis@5.3.2: - resolution: {integrity: sha512-1DKMMzlIHM02eBBVOFQ1+AolGjs6+xEcM4PDL7NqOS6szq7H9jSaEkIUH6/a5Hl241LzW6JLSiAbNvTQjUupUA==} - engines: {node: '>=12.22.0'} - dependencies: - '@ioredis/commands': 1.2.0 - cluster-key-slot: 1.1.2 - debug: 4.3.4(supports-color@8.1.1) - denque: 2.1.0 - lodash.defaults: 4.2.0 - lodash.isarguments: 3.1.0 - redis-errors: 1.2.0 - redis-parser: 3.0.0 - standard-as-callback: 2.1.0 - transitivePeerDependencies: - - supports-color - dev: false - /is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} @@ -1673,18 +1660,10 @@ packages: p-locate: 5.0.0 dev: true - /lodash.defaults@4.2.0: - resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} - dev: false - /lodash.includes@4.3.0: resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} dev: false - /lodash.isarguments@3.1.0: - resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==} - dev: false - /lodash.isboolean@3.0.3: resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} dev: false @@ -2227,18 +2206,6 @@ packages: engines: {node: '>= 12.13.0'} dev: false - /redis-errors@1.2.0: - resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==} - engines: {node: '>=4'} - dev: false - - /redis-parser@3.0.0: - resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==} - engines: {node: '>=4'} - dependencies: - redis-errors: 1.2.0 - dev: false - /regenerator-runtime@0.14.0: resolution: {integrity: sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==} dev: false @@ -2425,10 +2392,6 @@ packages: engines: {node: '>= 10.x'} dev: false - /standard-as-callback@2.1.0: - resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} - dev: false - /string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -2732,6 +2695,11 @@ packages: fsevents: 2.3.3 dev: true + /type-fest@4.10.2: + resolution: { integrity: sha512-anpAG63wSpdEbLwOqH8L84urkL6PiVIov3EMmgIhhThevh9aiMQov+6Btx0wldNcvm4wV+e2/Rt1QdDwKHFbHw== } + engines: { node: '>=16' } + dev: false + /typescript@5.3.3: resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} engines: {node: '>=14.17'} diff --git a/web/src/lib/auth.ts b/web/src/lib/auth.ts index 49c84e7..4407cc0 100644 --- a/web/src/lib/auth.ts +++ b/web/src/lib/auth.ts @@ -1,7 +1,7 @@ import jwt from 'jsonwebtoken'; import { randomUUID } from 'node:crypto'; import { sha256 } from '$lib/hash'; -import { getClient } from '$lib/redis'; +import { client } from '$lib/storage'; import 'dotenv/config'; export const AUTH_SECRET = process.env.AUTH_SECRET || sha256(randomUUID()); @@ -17,8 +17,7 @@ export async function generateToken(): Promise { }, AUTH_SECRET, ); - const client = getClient(); - await client.setex(token, oneHour, '1'); + client.setex(token, '1', oneHour); return token; } @@ -26,15 +25,14 @@ export async function verifyToken(token: string): Promise { try { const decode = jwt.verify(token, AUTH_SECRET); if (!decode) return false; - const client = getClient(); - const exists = await client.exists(token); - return exists === 1; + + const exists = client.exists(token); + return exists; } catch (e) { return false; } } export async function revokeToken(token: string): Promise { - const client = getClient(); - await client.del(token); + client.del(token); } diff --git a/web/src/lib/logger.ts b/web/src/lib/logger.ts index 1ef7078..34709b2 100644 --- a/web/src/lib/logger.ts +++ b/web/src/lib/logger.ts @@ -1,4 +1,4 @@ -import pino, { type Logger } from 'pino'; +import pino, { type Logger, type LoggerOptions } from 'pino'; import pretty from 'pino-pretty'; import { createWriteStream } from 'node:fs'; import { resolve } from 'node:path'; @@ -8,23 +8,33 @@ const LOG_LEVEL = process.env.LOG_LEVEL || 'trace'; const LOG_FILE_PATH = process.env.LOG_FILE_PATH || '/var/vlogs/web'; const LOG_COLORS = process.env.LOG_COLORS || 'true'; +const options: LoggerOptions = { + level: LOG_LEVEL, + customLevels: { + http: 10, + info: 30, + debug: 35, + warn: 40, + error: 50, + fatal: 60, + }, + useOnlyCustomLevels: true, +}; + +const jsonLevels = JSON.stringify(options.customLevels); +const levelsInString = jsonLevels.replaceAll('"', '').slice(0, -1).slice(1); + const prettyStream = pretty({ colorize: LOG_COLORS === 'true', + customLevels: levelsInString, }); -let logger: Logger = pino( - { - level: LOG_LEVEL, - }, - pino.multistream([prettyStream]), -); +let logger: Logger = pino(options, pino.multistream([prettyStream])); if (fsAccess(LOG_FILE_PATH)) { fsTouch(LOG_FILE_PATH).then(() => { logger = pino( - { - level: LOG_LEVEL, - }, + options, pino.multistream([ prettyStream, createWriteStream(resolve(LOG_FILE_PATH), { diff --git a/web/src/lib/redis.ts b/web/src/lib/redis.ts deleted file mode 100644 index 72991fc..0000000 --- a/web/src/lib/redis.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Redis } from 'ioredis'; - -export type RedisClient = Redis; - -let client: RedisClient | undefined; - -export function getClient(): RedisClient { - if (!client) { - throw new Error('Redis client not initialized'); - } - - return client; -} - -export function setClient(redis: RedisClient): void { - client = redis; -} - -if (process.env.NODE_ENV && ['development', 'production'].includes(process.env.NODE_ENV)) { - setClient( - new Redis({ - port: 6479, - }), - ); -} diff --git a/web/src/lib/storage.ts b/web/src/lib/storage.ts new file mode 100644 index 0000000..7f1c88d --- /dev/null +++ b/web/src/lib/storage.ts @@ -0,0 +1,8 @@ +import { Client, MSGPack } from '@litehex/storage-box'; +import { FsDriver } from '@litehex/storage-box/driver'; +import { resolve } from 'node:path'; + +const storagePath = resolve(process.cwd(), 'storage.pack'); +const driver = new FsDriver(storagePath, { parser: MSGPack }); + +export const client = new Client(driver); diff --git a/web/src/lib/utils.ts b/web/src/lib/utils.ts index 9b805ff..c714192 100644 --- a/web/src/lib/utils.ts +++ b/web/src/lib/utils.ts @@ -31,6 +31,10 @@ export function isObject(obj: object) { return Object.prototype.toString.call(obj) === '[object Object]'; } +export async function sleep(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + /** * Private IP Address Identifier in Regular Expression * diff --git a/web/src/lib/wireguard/index.ts b/web/src/lib/wireguard/index.ts index 5a1a0cd..0593326 100644 --- a/web/src/lib/wireguard/index.ts +++ b/web/src/lib/wireguard/index.ts @@ -4,12 +4,12 @@ import deepmerge from 'deepmerge'; import type { Peer, WgKey, WgServer } from '$lib/typings'; import Network from '$lib/network'; import { WG_PATH, WG_SEVER_PATH } from '$lib/constants'; -import { dynaJoin, isJson } from '$lib/utils'; +import { dynaJoin, isJson, sleep } from '$lib/utils'; import { getPeerConf } from '$lib/wireguard/utils'; import logger from '$lib/logger'; import { sha256 } from '$lib/hash'; import { fsAccess } from '$lib/fs-extra'; -import { getClient } from '$lib/redis'; +import { client } from '$lib/storage'; import { execa } from 'execa'; export class WGServer { @@ -17,30 +17,42 @@ export class WGServer { readonly peers: WGPeers; constructor(serverId: string) { - if (!serverId) throw new Error('serverId is required'); + if (!serverId) { + throw new Error('WGServer: id is required'); + } - if (!WGServer.exists(serverId)) throw new Error('server does not exists'); + if (!WGServer.exists(serverId)) { + throw new Error('WGServer: server not found'); + } this.id = serverId; this.peers = new WGPeers(this); } - static async exists(id: string): Promise { - const servers = await getServers(); - return servers.some((s) => s.id === id); + static exists(id: string): boolean { + const serverIds = getServers().map((s) => s.id); + + logger.debug({ + message: `WGServer:Exists: checking for server with id: ${id}`, + servers: serverIds, + }); + + return serverIds.includes(id); } async get(): Promise { if (!fsAccess(WG_PATH)) { + logger.debug('WGServer: get: creating wg path'); fs.mkdirSync(WG_PATH, { recursive: true, mode: 0o600 }); } const server = await findServer(this.id); if (!server) { - throw new Error('server not found'); + throw new Error('WGServer: get: server not found'); } if (!fsAccess(resolveConfigPath(server.confId))) { + logger.debug('WGServer: get: creating config file'); await this.writeConfigFile(server); } @@ -83,24 +95,19 @@ export class WGServer { const server = await this.get(); await this.stop(); + if (wgConfExists(server.confId)) { + logger.debug('WGServer:Remove: removing config file'); fs.unlinkSync(resolveConfigPath(server.confId)); } const index = await findServerIndex(this.id); if (typeof index !== 'number') { - logger.warn('findServerIndex: index not found'); + logger.warn('WGServer:Remove: server index not found'); return true; } - const client = getClient(); - const element = await client.lindex(WG_SEVER_PATH, index); - if (!element) { - logger.warn('remove: element not found'); - return true; - } - - await client.lrem(WG_SEVER_PATH, 1, element); + client.ldel(WG_SEVER_PATH, index); return true; } @@ -110,12 +117,14 @@ export class WGServer { const index = await findServerIndex(this.id); if (typeof index !== 'number') { - logger.warn('findServerIndex: index not found'); + logger.warn('WGServer:Update: server index not found'); return true; } - const client = getClient(); - const res = await client.lset( + console.log('WGServer:Update: updating server at index:', index); + console.log(client.lrange(WG_SEVER_PATH, 0, -1)); + + client.lset( WG_SEVER_PATH, index, JSON.stringify({ @@ -125,7 +134,7 @@ export class WGServer { }), ); - return res === 'OK'; + return true; } async writeConfigFile(wg: WgServer): Promise { @@ -154,7 +163,7 @@ export class WGServer { }; if (!hasInterface) { - logger.debug('GetUsage: interface does not exists'); + logger.debug('WGServer: GetUsage: interface does not exists'); return usages; } @@ -181,7 +190,7 @@ export class WGServer { static async getFreePeerIp(serverId: string): Promise { const server = await findServer(serverId); if (!server) { - logger.error('GetFreePeerIP: no server found'); + logger.error('WGSerevr: GetFreePeerIP: no server found'); return undefined; } @@ -196,7 +205,7 @@ export class WGServer { } } - logger.error('GetFreePeerIP: no free ip found'); + logger.error('WGServer: GetFreePeerIP: no free ip found'); return undefined; } } @@ -239,12 +248,11 @@ class WGPeers { const index = await findServerIndex(this.server.id); if (typeof index !== 'number') { - logger.warn('findServerIndex: index not found'); + logger.warn('WGPeers:Add: server index not found'); return true; } - const client = getClient(); - await client.lset( + client.lset( WG_SEVER_PATH, index, JSON.stringify({ @@ -263,16 +271,15 @@ class WGPeers { async remove(publicKey: string): Promise { const server = await this.server.get(); - const peers = await wgPeersStr(server.confId); + const peers = wgPeersStr(server.confId); const index = await findServerIndex(this.server.id); if (typeof index !== 'number') { - logger.warn('findServerIndex: index not found'); + logger.warn('WGPeers:Remove: server index not found'); return true; } - const client = getClient(); - await client.lset( + client.lset( WG_SEVER_PATH, index, JSON.stringify({ @@ -283,7 +290,7 @@ class WGPeers { const peerIndex = peers.findIndex((p) => p.includes(`PublicKey = ${publicKey}`)); if (peerIndex === -1) { - logger.warn('removePeer: no peer found'); + logger.warn('WGPeers:Remove: peer not found'); return false; } @@ -307,7 +314,7 @@ class WGPeers { const index = await findServerIndex(this.server.id); if (typeof index !== 'number') { - logger.warn('findServerIndex: index not found'); + logger.warn('WGPeers:Update: server index not found'); return true; } @@ -316,8 +323,7 @@ class WGPeers { return deepmerge(p, update); }); - const client = getClient(); - await client.lset(WG_SEVER_PATH, index, JSON.stringify({ ...server, peers: updatedPeers })); + client.lset(WG_SEVER_PATH, index, JSON.stringify({ ...server, peers: updatedPeers })); await this.storePeers(publicKey, updatedPeers); if (server.status === 'up') { @@ -336,14 +342,16 @@ class WGPeers { async generateConfig(peerId: string): Promise { const server = await findServer(this.server.id); if (!server) { - logger.error('generatePeerConfig: server not found'); + logger.error('WGPeers:GeneratePeerConfig: server not found'); return undefined; } + const peer = server.peers.find((p) => p.id === peerId); if (!peer) { - logger.error('generatePeerConfig: peer not found'); + logger.error('WGPeers:GeneratePeerConfig: peer not found'); return undefined; } + return await getPeerConf({ ...peer, serverPublicKey: server.publicKey, @@ -487,11 +495,14 @@ async function syncServers(): Promise { // read all confs const servers = await Promise.all(confs.map((f) => readWgConf(parseInt(f.match(reg)![1])))); - const client = getClient(); // remove old servers - await client.del(WG_SEVER_PATH); + client.del(WG_SEVER_PATH); + // save all servers to redis - await client.lpush(WG_SEVER_PATH, ...servers.map((s) => JSON.stringify(s))); + client.lpush( + WG_SEVER_PATH, + servers.map((s) => JSON.stringify(s)), + ); return true; } @@ -519,16 +530,19 @@ interface GenerateWgServerParams { port: number; dns?: string; mtu?: number; - insertDb?: boolean; } export async function generateWgServer(config: GenerateWgServerParams): Promise { const { privateKey, publicKey } = await generateWgKey(); - // inside redis create a config list + // Inside storage create a config list const confId = await getNextFreeConfId(); const uuid = crypto.randomUUID(); + logger.debug( + `WireGuard: GenerateWgServer: creating server with id: ${uuid} and confId: ${confId}`, + ); + let server: WgServer = { id: uuid, confId, @@ -550,7 +564,7 @@ export async function generateWgServer(config: GenerateWgServerParams): Promise< status: 'up', }; - // check if address or port are already reserved + // Check if address or port is already reserved if (await isIPReserved(config.address)) { throw new Error(`Address ${config.address} is already reserved!`); } @@ -565,17 +579,20 @@ export async function generateWgServer(config: GenerateWgServerParams): Promise< server.postDown = iptables.down; // save server config - if (false !== config.insertDb) { - const client = getClient(); - await client.lpush(WG_SEVER_PATH, JSON.stringify(server)); - } + logger.debug('WireGuard: GenerateWgServer: saving server to storage'); + logger.debug(server); + client.lpush(WG_SEVER_PATH, JSON.stringify(server)); const CONFIG_PATH = resolveConfigPath(confId); // save server config to disk + logger.debug('WireGuard: GenerateWgServer: writing config file'); fs.writeFileSync(CONFIG_PATH, await genServerConf(server), { mode: 0o600 }); + await sleep(50); + // updating hash of the config + logger.debug('WireGuard: GenerateWgServer: updating config hash'); const wg = new WGServer(uuid); await wg.update({ confHash: getConfigHash(confId) }); @@ -590,20 +607,17 @@ export async function generateWgServer(config: GenerateWgServerParams): Promise< } export async function isIPReserved(ip: string): Promise { - const addresses = (await getServers()).map((s) => s.address); + const addresses = getServers().map((s) => s.address); return addresses.includes(ip); } export async function isPortReserved(port: number): Promise { - const inUsePorts = [ - await Network.inUsePorts(), - (await getServers()).map((s) => Number(s.listen)), - ].flat(); + const inUsePorts = [await Network.inUsePorts(), getServers().map((s) => Number(s.listen))].flat(); return inUsePorts.includes(port); } export async function isConfigIdReserved(id: number): Promise { - const ids = (await getServers()).map((s) => s.confId); + const ids = getServers().map((s) => s.confId); return ids.includes(id); } @@ -619,6 +633,7 @@ export async function getNextFreeConfId(): Promise { export function getConfigHash(confId: number): string | undefined { if (!wgConfExists(confId)) { + logger.debug('WireGuard: GetConfigHash: config does not exists. ConfId:', confId); return undefined; } @@ -645,10 +660,21 @@ export function maxConfId(): number { return Math.max(0, ...ids); } -export async function getServers(): Promise { - const client = getClient(); - const rawServers = await client.lrange(WG_SEVER_PATH, 0, -1); - return rawServers.map((s) => JSON.parse(s)); +export function getServers(): WgServer[] { + const rawServers = (client.list(WG_SEVER_PATH) || []) as string[]; + return rawServers.map((s) => { + if (isJson(s)) { + return JSON.parse(s); + } + + if (typeof s === 'object') { + return s; + } + + logger.warn('WireGuard: GetServers: invalid server found'); + + return null; + }); } export async function findServerIndex(id: string): Promise { diff --git a/web/src/routes/+page.server.ts b/web/src/routes/+page.server.ts index a53a127..15b05a3 100644 --- a/web/src/routes/+page.server.ts +++ b/web/src/routes/+page.server.ts @@ -15,7 +15,7 @@ import logger from '$lib/logger'; export const load: PageServerLoad = async () => { return { - servers: (await getServers()).map((s) => s), + servers: getServers(), form: superValidate(CreateServerSchema), }; }; diff --git a/web/src/routes/+page.svelte b/web/src/routes/+page.svelte index 9546290..1e9eba5 100644 --- a/web/src/routes/+page.svelte +++ b/web/src/routes/+page.svelte @@ -70,12 +70,6 @@ - - - - - - diff --git a/web/src/routes/login/+layout.svelte b/web/src/routes/login/+layout.svelte index 3962c68..7279d85 100644 --- a/web/src/routes/login/+layout.svelte +++ b/web/src/routes/login/+layout.svelte @@ -1,12 +1,11 @@
- WireAdmin + WireAdmin

WireAdmin