mirror of
https://github.com/wireadmin/wireadmin
synced 2025-02-26 05:48:44 +00:00
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:
parent
14624a6c12
commit
4875f6dea2
@ -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
|
||||
|
||||
|
@ -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"]
|
||||
|
@ -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",
|
||||
|
@ -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'}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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), {
|
||||
|
@ -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
8
web/src/lib/storage.ts
Normal 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);
|
@ -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
|
||||
*
|
||||
|
@ -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> {
|
||||
|
@ -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),
|
||||
};
|
||||
};
|
||||
|
@ -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>
|
||||
|
@ -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'}>
|
||||
|
Loading…
Reference in New Issue
Block a user