Browse Source

migrate to sqlite

pull/1619/head
Bernd Storath 5 months ago
parent
commit
40bbc27331
  1. 8
      src/app/pages/admin/hooks.vue
  2. 12
      src/app/pages/clients/[id].vue
  3. 6
      src/server/api/wireguard/backup.get.ts
  4. 6
      src/server/api/wireguard/restore.put.ts
  5. 36
      src/server/database/repositories/client/types.ts
  6. 31
      src/server/database/repositories/metrics/service.ts
  7. 10
      src/server/database/repositories/oneTimeLink/service.ts
  8. 13
      src/server/database/repositories/oneTimeLink/types.ts
  9. 13
      src/server/database/repositories/user/types.ts
  10. 7
      src/server/database/repositories/userConfig/types.ts
  11. 4
      src/server/database/sqlite.ts
  12. 6
      src/server/routes/cnf/[oneTimeLink].ts
  13. 6
      src/server/routes/metrics/index.get.ts
  14. 6
      src/server/routes/metrics/json.get.ts
  15. 73
      src/server/utils/WireGuard.ts
  16. 74
      src/server/utils/metrics.ts
  17. 4
      src/server/utils/types.ts

8
src/app/pages/admin/hooks.vue

@ -2,10 +2,10 @@
<main v-if="data"> <main v-if="data">
<FormElement @submit.prevent="submit"> <FormElement @submit.prevent="submit">
<FormGroup> <FormGroup>
<FormTextField id="PreUp" v-model="data.PreUp" label="PreUp" /> <FormTextField id="PreUp" v-model="data.preUp" label="PreUp" />
<FormTextField id="PostUp" v-model="data.PostUp" label="PostUp" /> <FormTextField id="PostUp" v-model="data.postUp" label="PostUp" />
<FormTextField id="PreDown" v-model="data.PreDown" label="PreDown" /> <FormTextField id="PreDown" v-model="data.preDown" label="PreDown" />
<FormTextField id="PostDown" v-model="data.PostDown" label="PostDown" /> <FormTextField id="PostDown" v-model="data.postDown" label="PostDown" />
</FormGroup> </FormGroup>
<FormGroup> <FormGroup>
<FormHeading>Actions</FormHeading> <FormHeading>Actions</FormHeading>

12
src/app/pages/clients/[id].vue

@ -25,13 +25,13 @@
<FormGroup> <FormGroup>
<FormHeading>Address</FormHeading> <FormHeading>Address</FormHeading>
<FormTextField <FormTextField
id="address4" id="ipv4Address"
v-model.trim="data.address4" v-model.trim="data.ipv4Address"
label="IPv4" label="IPv4"
/> />
<FormTextField <FormTextField
id="address6" id="ipv6Address"
v-model.trim="data.address6" v-model.trim="data.ipv6Address"
label="IPv6" label="IPv6"
/> />
</FormGroup> </FormGroup>
@ -42,8 +42,8 @@
<FormGroup> <FormGroup>
<FormHeading>Server Allowed IPs</FormHeading> <FormHeading>Server Allowed IPs</FormHeading>
<FormArrayField <FormArrayField
v-model="data.serverAllowedIPs" v-model="data.serverAllowedIps"
name="serverAllowedIPs" name="serverAllowedIps"
/> />
</FormGroup> </FormGroup>
<FormGroup></FormGroup> <FormGroup></FormGroup>

6
src/server/api/wireguard/backup.get.ts

@ -1,9 +1,9 @@
export default definePermissionEventHandler( export default definePermissionEventHandler(
actions.ADMIN, actions.ADMIN,
async ({ event }) => { async (/*{ event }*/) => {
const config = await WireGuard.backupConfiguration(); /*const config = await WireGuard.backupConfiguration();
setHeader(event, 'Content-Disposition', 'attachment; filename="wg0.json"'); setHeader(event, 'Content-Disposition', 'attachment; filename="wg0.json"');
setHeader(event, 'Content-Type', 'text/json'); setHeader(event, 'Content-Type', 'text/json');
return config; return config;*/
} }
); );

6
src/server/api/wireguard/restore.put.ts

@ -1,8 +1,8 @@
export default definePermissionEventHandler( export default definePermissionEventHandler(
actions.ADMIN, actions.ADMIN,
async ({ event }) => { async (/*{ event }*/) => {
const { file } = await readValidatedBody(event, validateZod(fileType)); /*const { file } = await readValidatedBody(event, validateZod(fileType));
await WireGuard.restoreConfiguration(file); await WireGuard.restoreConfiguration(file);
return { success: true }; return { success: true };*/
} }
); );

36
src/server/database/repositories/client/types.ts

@ -1,12 +1,12 @@
import type { InferSelectModel } from 'drizzle-orm'; import type { InferSelectModel } from 'drizzle-orm';
import { zod } from '#imports'; import z from 'zod';
import type { client } from './schema'; import type { client } from './schema';
const schemaForType = const schemaForType =
<T>() => <T>() =>
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
<S extends zod.ZodType<T, any, any>>(arg: S) => { <S extends z.ZodType<T, any, any>>(arg: S) => {
return arg; return arg;
}; };
@ -24,65 +24,65 @@ export type UpdateClientType = Omit<
'privateKey' | 'publicKey' | 'preSharedKey' 'privateKey' | 'publicKey' | 'preSharedKey'
>; >;
const name = zod const name = z
.string({ message: 'zod.client.name' }) .string({ message: 'zod.client.name' })
.min(1, 'zod.client.nameMin') .min(1, 'zod.client.nameMin')
.pipe(safeStringRefine); .pipe(safeStringRefine);
const expiresAt = zod const expiresAt = z
.string({ message: 'zod.client.expireDate' }) .string({ message: 'zod.client.expireDate' })
.min(1, 'zod.client.expireDateMin') .min(1, 'zod.client.expireDateMin')
.pipe(safeStringRefine) .pipe(safeStringRefine)
.nullable(); .nullable();
const address = zod const address = z
.string({ message: 'zod.client.address' }) .string({ message: 'zod.client.address' })
.min(1, { message: 'zod.client.addressMin' }) .min(1, { message: 'zod.client.addressMin' })
.pipe(safeStringRefine); .pipe(safeStringRefine);
const address4 = zod const address4 = z
.string({ message: 'zod.client.address4' }) .string({ message: 'zod.client.address4' })
.min(1, { message: 'zod.client.address4Min' }) .min(1, { message: 'zod.client.address4Min' })
.pipe(safeStringRefine); .pipe(safeStringRefine);
const address6 = zod const address6 = z
.string({ message: 'zod.client.address6' }) .string({ message: 'zod.client.address6' })
.min(1, { message: 'zod.client.address6Min' }) .min(1, { message: 'zod.client.address6Min' })
.pipe(safeStringRefine); .pipe(safeStringRefine);
const allowedIps = zod const allowedIps = z
.array(address, { message: 'zod.client.allowedIps' }) .array(address, { message: 'zod.client.allowedIps' })
.min(1, { message: 'zod.client.allowedIpsMin' }); .min(1, { message: 'zod.client.allowedIpsMin' });
const serverAllowedIps = zod.array(address, { const serverAllowedIps = z.array(address, {
message: 'zod.serverAllowedIps', message: 'zod.serverAllowedIps',
}); });
const mtu = zod const mtu = z
.number({ message: 'zod.client.mtu' }) .number({ message: 'zod.client.mtu' })
.min(1280, { message: 'zod.client.mtuMin' }) .min(1280, { message: 'zod.client.mtuMin' })
.max(9000, { message: 'zod.client.mtuMax' }); .max(9000, { message: 'zod.client.mtuMax' });
const persistentKeepalive = zod const persistentKeepalive = z
.number({ message: 'zod.client.persistentKeepalive' }) .number({ message: 'zod.client.persistentKeepalive' })
.min(0, 'zod.client.persistentKeepaliveMin') .min(0, 'zod.client.persistentKeepaliveMin')
.max(65535, 'zod.client.persistentKeepaliveMax'); .max(65535, 'zod.client.persistentKeepaliveMax');
const enabled = zod.boolean({ message: 'zod.enabled' }); const enabled = z.boolean({ message: 'zod.enabled' });
const dns = zod const dns = z
.array(address, { message: 'zod.client.dns' }) .array(address, { message: 'zod.client.dns' })
.min(1, 'zod.client.dnsMin'); .min(1, 'zod.client.dnsMin');
export const ClientCreateSchema = zod.object({ export const ClientCreateSchema = z.object({
name: name, name: name,
expiresAt: expiresAt, expiresAt: expiresAt,
}); });
export type ClientCreateType = zod.infer<typeof ClientCreateSchema>; export type ClientCreateType = z.infer<typeof ClientCreateSchema>;
export const ClientUpdateSchema = schemaForType<UpdateClientType>()( export const ClientUpdateSchema = schemaForType<UpdateClientType>()(
zod.object({ z.object({
name: name, name: name,
enabled: enabled, enabled: enabled,
expiresAt: expiresAt, expiresAt: expiresAt,
@ -96,8 +96,8 @@ export const ClientUpdateSchema = schemaForType<UpdateClientType>()(
}) })
); );
const clientId = zod.number({ message: 'zod.client.id' }); const clientId = z.number({ message: 'zod.client.id' });
export const ClientGetSchema = zod.object({ export const ClientGetSchema = z.object({
clientId: clientId, clientId: clientId,
}); });

31
src/server/database/repositories/metrics/service.ts

@ -0,0 +1,31 @@
import type { DBType } from '#db/sqlite';
import { eq, sql } from 'drizzle-orm';
import { prometheus } from './schema';
function createPreparedStatement(db: DBType) {
return {
get: db.query.prometheus
.findFirst({ where: eq(prometheus.id, sql.placeholder('interface')) })
.prepare(),
};
}
export class PrometheusService {
#statements: ReturnType<typeof createPreparedStatement>;
constructor(db: DBType) {
this.#statements = createPreparedStatement(db);
}
get(infName: string) {
return this.#statements.get.execute({ interface: infName });
}
}
export class MetricsService {
prometheus: PrometheusService;
constructor(db: DBType) {
this.prometheus = new PrometheusService(db);
}
}

10
src/server/database/repositories/oneTimeLink/service.ts

@ -18,6 +18,11 @@ function createPreparedStatement(db: DBType) {
expiresAt: sql.placeholder('expiresAt'), expiresAt: sql.placeholder('expiresAt'),
}) })
.prepare(), .prepare(),
erase: db
.update(oneTimeLink)
.set({ expiresAt: sql.placeholder('expiresAt') as never as string })
.where(eq(oneTimeLink.clientId, sql.placeholder('id')))
.prepare(),
}; };
} }
@ -39,4 +44,9 @@ export class OneTimeLinkService {
return this.#statements.create.execute({ id, oneTimeLink, expiresAt }); return this.#statements.create.execute({ id, oneTimeLink, expiresAt });
} }
erase(id: ID) {
const expiresAt = Date.now() + 10 * 1000;
return this.#statements.erase.execute({ id, expiresAt });
}
} }

13
src/server/database/repositories/oneTimeLink/types.ts

@ -1,4 +1,17 @@
import type { InferSelectModel } from 'drizzle-orm'; import type { InferSelectModel } from 'drizzle-orm';
import type { oneTimeLink } from './schema'; import type { oneTimeLink } from './schema';
import { z } from 'zod';
export type OneTimeLinkType = InferSelectModel<typeof oneTimeLink>; export type OneTimeLinkType = InferSelectModel<typeof oneTimeLink>;
const oneTimeLinkType = z
.string({ message: 'zod.otl' })
.min(1, 'zod.otlMin')
.pipe(safeStringRefine);
export const OneTimeLinkGetSchema = z.object(
{
oneTimeLink: oneTimeLinkType,
},
{ message: objectMessage }
);

13
src/server/database/repositories/user/types.ts

@ -1,14 +1,15 @@
import type { InferSelectModel } from 'drizzle-orm'; import type { InferSelectModel } from 'drizzle-orm';
import type { user } from './schema'; import type { user } from './schema';
import z from 'zod';
export type UserType = InferSelectModel<typeof user>; export type UserType = InferSelectModel<typeof user>;
const username = zod const username = z
.string({ message: 'zod.user.username' }) .string({ message: 'zod.user.username' })
.min(8, 'zod.user.usernameMin') .min(8, 'zod.user.usernameMin')
.pipe(safeStringRefine); .pipe(safeStringRefine);
const password = zod const password = z
.string({ message: 'zod.user.password' }) .string({ message: 'zod.user.password' })
.min(12, 'zod.user.passwordMin') .min(12, 'zod.user.passwordMin')
.regex(/[A-Z]/, 'zod.user.passwordUppercase') .regex(/[A-Z]/, 'zod.user.passwordUppercase')
@ -17,9 +18,9 @@ const password = zod
.regex(/[!@#$%^&*(),.?":{}|<>]/, 'zod.user.passwordSpecial') .regex(/[!@#$%^&*(),.?":{}|<>]/, 'zod.user.passwordSpecial')
.pipe(safeStringRefine); .pipe(safeStringRefine);
const remember = zod.boolean({ message: 'zod.user.remember' }); const remember = z.boolean({ message: 'zod.user.remember' });
export const UserLoginSchema = zod.object( export const UserLoginSchema = z.object(
{ {
username: username, username: username,
password: password, password: password,
@ -28,11 +29,11 @@ export const UserLoginSchema = zod.object(
{ message: objectMessage } { message: objectMessage }
); );
const accept = zod.boolean().refine((val) => val === true, { const accept = z.boolean().refine((val) => val === true, {
message: 'zod.user.accept', message: 'zod.user.accept',
}); });
export const UserSetupType = zod.object( export const UserSetupType = z.object(
{ {
username: username, username: username,
password: password, password: password,

7
src/server/database/repositories/userConfig/types.ts

@ -1,19 +1,20 @@
import type { InferSelectModel } from 'drizzle-orm'; import type { InferSelectModel } from 'drizzle-orm';
import type { userConfig } from './schema'; import type { userConfig } from './schema';
import z from 'zod';
export type UserConfigType = InferSelectModel<typeof userConfig>; export type UserConfigType = InferSelectModel<typeof userConfig>;
const host = zod const host = z
.string({ message: 'zod.userConfig.host' }) .string({ message: 'zod.userConfig.host' })
.min(1, 'zod.userConfig.hostMin') .min(1, 'zod.userConfig.hostMin')
.pipe(safeStringRefine); .pipe(safeStringRefine);
const port = zod const port = z
.number({ message: 'zod.userConfig.port' }) .number({ message: 'zod.userConfig.port' })
.min(1, 'zod.userConfig.portMin') .min(1, 'zod.userConfig.portMin')
.max(65535, 'zod.userConfig.portMax'); .max(65535, 'zod.userConfig.portMax');
export const UserConfigSetupType = zod.object({ export const UserConfigSetupType = z.object({
host: host, host: host,
port: port, port: port,
}); });

4
src/server/database/sqlite.ts

@ -11,6 +11,7 @@ import { UserConfigService } from './repositories/userConfig/service';
import { InterfaceService } from './repositories/interface/service'; import { InterfaceService } from './repositories/interface/service';
import { HooksService } from './repositories/hooks/service'; import { HooksService } from './repositories/hooks/service';
import { OneTimeLinkService } from './repositories/oneTimeLink/service'; import { OneTimeLinkService } from './repositories/oneTimeLink/service';
import { MetricsService } from './repositories/metrics/service';
const DB_DEBUG = debug('Database'); const DB_DEBUG = debug('Database');
@ -30,6 +31,8 @@ class DBService {
interfaces: InterfaceService; interfaces: InterfaceService;
hooks: HooksService; hooks: HooksService;
oneTimeLinks: OneTimeLinkService; oneTimeLinks: OneTimeLinkService;
metrics: MetricsService;
constructor(db: DBType) { constructor(db: DBType) {
this.clients = new ClientService(db); this.clients = new ClientService(db);
this.general = new GeneralService(db); this.general = new GeneralService(db);
@ -38,6 +41,7 @@ class DBService {
this.interfaces = new InterfaceService(db); this.interfaces = new InterfaceService(db);
this.hooks = new HooksService(db); this.hooks = new HooksService(db);
this.oneTimeLinks = new OneTimeLinkService(db); this.oneTimeLinks = new OneTimeLinkService(db);
this.metrics = new MetricsService(db);
} }
} }

6
src/server/routes/cnf/[oneTimeLink].ts

@ -1,7 +1,9 @@
import { OneTimeLinkGetSchema } from '#db/repositories/oneTimeLink/types';
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
const { oneTimeLink } = await getValidatedRouterParams( const { oneTimeLink } = await getValidatedRouterParams(
event, event,
validateZod(oneTimeLinkType) validateZod(OneTimeLinkGetSchema)
); );
const clients = await WireGuard.getClients(); const clients = await WireGuard.getClients();
const client = clients.find( const client = clients.find(
@ -15,7 +17,7 @@ export default defineEventHandler(async (event) => {
} }
const clientId = client.id; const clientId = client.id;
const config = await WireGuard.getClientConfiguration({ clientId }); const config = await WireGuard.getClientConfiguration({ clientId });
await WireGuard.eraseOneTimeLink({ clientId }); await Database.oneTimeLinks.erase(clientId);
setHeader( setHeader(
event, event,
'Content-Disposition', 'Content-Disposition',

6
src/server/routes/metrics/index.get.ts

@ -1,8 +1,8 @@
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
// TODO: check password // TODO: check password
const system = await Database.system.get(); const prometheus = await Database.metrics.prometheus.get('wg0');
if (!system.metrics.prometheus.enabled) { if (!prometheus) {
throw createError({ throw createError({
statusCode: 400, statusCode: 400,
message: 'Prometheus metrics are not enabled', message: 'Prometheus metrics are not enabled',
@ -10,5 +10,5 @@ export default defineEventHandler(async (event) => {
} }
setHeader(event, 'Content-Type', 'text/plain'); setHeader(event, 'Content-Type', 'text/plain');
return WireGuard.getMetrics(); return getPrometheusResponse();
}); });

6
src/server/routes/metrics/json.get.ts

@ -1,13 +1,13 @@
export default defineEventHandler(async () => { export default defineEventHandler(async () => {
// TODO: check password // TODO: check password
const system = await Database.system.get(); const prometheus = await Database.metrics.prometheus.get('wg0');
if (!system.metrics.prometheus.enabled) { if (!prometheus) {
throw createError({ throw createError({
statusCode: 400, statusCode: 400,
message: 'Prometheus metrics are not enabled', message: 'Prometheus metrics are not enabled',
}); });
} }
return WireGuard.getMetricsJSON(); return getMetricsJSON();
}); });

73
src/server/utils/WireGuard.ts

@ -217,79 +217,6 @@ class WireGuard {
await this.saveConfig(); await this.saveConfig();
} }
async getMetrics() {
const clients = await this.getClients();
let wireguardPeerCount = 0;
let wireguardEnabledPeersCount = 0;
let wireguardConnectedPeersCount = 0;
let wireguardSentBytes = '';
let wireguardReceivedBytes = '';
let wireguardLatestHandshakeSeconds = '';
for (const client of clients) {
wireguardPeerCount++;
if (client.enabled === true) {
wireguardEnabledPeersCount++;
}
if (client.endpoint !== null) {
wireguardConnectedPeersCount++;
}
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';
returnText += '\n# HELP wireguard_configured_peers\n';
returnText += '# TYPE wireguard_configured_peers gauge\n';
returnText += `wireguard_configured_peers{interface="wg0"} ${wireguardPeerCount}\n`;
returnText += '\n# HELP wireguard_enabled_peers\n';
returnText += '# TYPE wireguard_enabled_peers gauge\n';
returnText += `wireguard_enabled_peers{interface="wg0"} ${wireguardEnabledPeersCount}\n`;
returnText += '\n# HELP wireguard_connected_peers\n';
returnText += '# TYPE wireguard_connected_peers gauge\n';
returnText += `wireguard_connected_peers{interface="wg0"} ${wireguardConnectedPeersCount}\n`;
returnText += '\n# HELP wireguard_sent_bytes Bytes sent to the peer\n';
returnText += '# TYPE wireguard_sent_bytes counter\n';
returnText += `${wireguardSentBytes}`;
returnText +=
'\n# HELP wireguard_received_bytes Bytes received from the peer\n';
returnText += '# TYPE wireguard_received_bytes counter\n';
returnText += `${wireguardReceivedBytes}`;
returnText +=
'\n# HELP wireguard_latest_handshake_seconds UNIX timestamp seconds of the last handshake\n';
returnText += '# TYPE wireguard_latest_handshake_seconds gauge\n';
returnText += `${wireguardLatestHandshakeSeconds}`;
return returnText;
}
async getMetricsJSON() {
const clients = await this.getClients();
let wireguardPeerCount = 0;
let wireguardEnabledPeersCount = 0;
let wireguardConnectedPeersCount = 0;
for (const client of clients) {
wireguardPeerCount++;
if (client.enabled === true) {
wireguardEnabledPeersCount++;
}
if (client.endpoint !== null) {
wireguardConnectedPeersCount++;
}
}
return {
wireguard_configured_peers: wireguardPeerCount,
wireguard_enabled_peers: wireguardEnabledPeersCount,
wireguard_connected_peers: wireguardConnectedPeersCount,
};
}
} }
export default new WireGuard(); export default new WireGuard();

74
src/server/utils/metrics.ts

@ -0,0 +1,74 @@
// TODO: rewrite
export async function getPrometheusResponse() {
const clients = await WireGuard.getClients();
let wireguardPeerCount = 0;
let wireguardEnabledPeersCount = 0;
let wireguardConnectedPeersCount = 0;
let wireguardSentBytes = '';
let wireguardReceivedBytes = '';
let wireguardLatestHandshakeSeconds = '';
for (const client of clients) {
wireguardPeerCount++;
if (client.enabled === true) {
wireguardEnabledPeersCount++;
}
if (client.endpoint !== null) {
wireguardConnectedPeersCount++;
}
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';
returnText += '\n# HELP wireguard_configured_peers\n';
returnText += '# TYPE wireguard_configured_peers gauge\n';
returnText += `wireguard_configured_peers{interface="wg0"} ${wireguardPeerCount}\n`;
returnText += '\n# HELP wireguard_enabled_peers\n';
returnText += '# TYPE wireguard_enabled_peers gauge\n';
returnText += `wireguard_enabled_peers{interface="wg0"} ${wireguardEnabledPeersCount}\n`;
returnText += '\n# HELP wireguard_connected_peers\n';
returnText += '# TYPE wireguard_connected_peers gauge\n';
returnText += `wireguard_connected_peers{interface="wg0"} ${wireguardConnectedPeersCount}\n`;
returnText += '\n# HELP wireguard_sent_bytes Bytes sent to the peer\n';
returnText += '# TYPE wireguard_sent_bytes counter\n';
returnText += `${wireguardSentBytes}`;
returnText +=
'\n# HELP wireguard_received_bytes Bytes received from the peer\n';
returnText += '# TYPE wireguard_received_bytes counter\n';
returnText += `${wireguardReceivedBytes}`;
returnText +=
'\n# HELP wireguard_latest_handshake_seconds UNIX timestamp seconds of the last handshake\n';
returnText += '# TYPE wireguard_latest_handshake_seconds gauge\n';
returnText += `${wireguardLatestHandshakeSeconds}`;
return returnText;
}
export async function getMetricsJSON() {
const clients = await WireGuard.getClients();
let wireguardPeerCount = 0;
let wireguardEnabledPeersCount = 0;
let wireguardConnectedPeersCount = 0;
for (const client of clients) {
wireguardPeerCount++;
if (client.enabled === true) {
wireguardEnabledPeersCount++;
}
if (client.endpoint !== null) {
wireguardConnectedPeersCount++;
}
}
return {
wireguard_configured_peers: wireguardPeerCount,
wireguard_enabled_peers: wireguardEnabledPeersCount,
wireguard_connected_peers: wireguardConnectedPeersCount,
};
}

4
src/server/utils/types.ts

@ -2,8 +2,6 @@ import type { ZodSchema } from 'zod';
import z from 'zod'; import z from 'zod';
import type { H3Event, EventHandlerRequest } from 'h3'; import type { H3Event, EventHandlerRequest } from 'h3';
export { default as zod } from 'zod';
export const objectMessage = 'zod.body'; export const objectMessage = 'zod.body';
export const safeStringRefine = z export const safeStringRefine = z
@ -28,7 +26,7 @@ export function validateZod<T>(
return await schema.parseAsync(data); return await schema.parseAsync(data);
} catch (error) { } catch (error) {
let message = 'Unexpected Error'; let message = 'Unexpected Error';
if (error instanceof zod.ZodError) { if (error instanceof z.ZodError) {
message = error.issues message = error.issues
.map((v) => { .map((v) => {
let m = v.message; let m = v.message;

Loading…
Cancel
Save