Minor update:

- Using a native solution instead of `Redis`
- Forcing logger to use custom log levels
- Fixing a few minor issues about generating configs
This commit is contained in:
Shahrad Elahi 2024-02-07 18:45:17 +03:30
parent 14624a6c12
commit 4875f6dea2
13 changed files with 161 additions and 176 deletions

View File

@ -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

View File

@ -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"]

View File

@ -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",

View File

@ -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'}

View File

@ -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<string> {
},
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<boolean> {
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<void> {
const client = getClient();
await client.del(token);
client.del(token);
}

View File

@ -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), {

View File

@ -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,
}),
);
}

8
web/src/lib/storage.ts Normal file
View File

@ -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);

View File

@ -31,6 +31,10 @@ export function isObject(obj: object) {
return Object.prototype.toString.call(obj) === '[object Object]';
}
export async function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
/**
* Private IP Address Identifier in Regular Expression
*

View File

@ -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<boolean> {
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<WgServer> {
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<void> {
@ -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<string | undefined> {
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<boolean> {
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<string | undefined> {
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<boolean> {
// 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<string> {
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<boolean> {
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<boolean> {
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<boolean> {
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<number> {
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<WgServer[]> {
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<number | undefined> {

View File

@ -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),
};
};

View File

@ -70,12 +70,6 @@
<i class={'fa-solid fa-onion text-purple-700 text-xl'} />
</svelte:fragment>
</Service>
<Service name="Redis" slug="redis">
<svelte:fragment slot="icon">
<i class={'fa-solid fa-database text-red-700 text-xl'} />
</svelte:fragment>
</Service>
</CardContent>
</Card>
</div>

View File

@ -1,12 +1,11 @@
<script lang="ts">
import PageFooter from '$lib/components/page/PageFooter.svelte';
import Logo from '../../../static/logo.png';
</script>
<div class={'w-full min-h-screen flex justify-center px-2 md:px-6 py-2'}>
<div class={'w-full mx-auto max-w-3xl flex flex-col items-center gap-y-3.5'}>
<header class={'flex items-center gap-x-2 text-3xl font-medium py-4'}>
<img src={Logo} alt="WireAdmin" width="40" height="40" />
<img src={'/logo.png'} alt="WireAdmin" width="40" height="40" />
<h1>WireAdmin</h1>
</header>
<main class={'py-4'}>