mirror of https://github.com/wg-easy/wg-easy
Browse Source
* check metrics password * rewrite prometheus and json metric endpoints * move metrics to general metrics is not per interface * change metrics settings in admin panel * add i18n keyspull/1657/head
committed by
GitHub
30 changed files with 391 additions and 346 deletions
@ -0,0 +1,26 @@ |
|||||
|
<template> |
||||
|
<Label :for="id" class="font-semibold md:align-middle md:leading-10"> |
||||
|
{{ label }} |
||||
|
</Label> |
||||
|
<input |
||||
|
:id="id" |
||||
|
v-model.trim="data" |
||||
|
:name="id" |
||||
|
type="text" |
||||
|
class="rounded-lg border-2 border-gray-100 text-gray-500 focus:border-red-800 focus:outline-0 focus:ring-0 dark:border-neutral-800 dark:bg-neutral-700 dark:text-neutral-200 dark:placeholder:text-neutral-400" |
||||
|
/> |
||||
|
</template> |
||||
|
|
||||
|
<script lang="ts" setup> |
||||
|
defineProps<{ id: string; label: string }>(); |
||||
|
|
||||
|
const data = defineModel<string | null>({ |
||||
|
set(value) { |
||||
|
const temp = value?.trim() ?? null; |
||||
|
if (temp === '') { |
||||
|
return null; |
||||
|
} |
||||
|
return temp; |
||||
|
}, |
||||
|
}); |
||||
|
</script> |
@ -1,3 +0,0 @@ |
|||||
<template><div></div></template> |
|
||||
|
|
||||
<script lang="ts" setup></script> |
|
@ -1,6 +1,4 @@ |
|||||
export default definePermissionEventHandler(actions.ADMIN, async () => { |
export default definePermissionEventHandler(actions.ADMIN, async () => { |
||||
const sessionConfig = await Database.general.getSessionConfig(); |
const generalConfig = await Database.general.getConfig(); |
||||
return { |
return generalConfig; |
||||
sessionTimeout: sessionConfig.sessionTimeout, |
|
||||
}; |
|
||||
}); |
}); |
||||
|
@ -1,21 +0,0 @@ |
|||||
import { sql } from 'drizzle-orm'; |
|
||||
import { sqliteTable, text } from 'drizzle-orm/sqlite-core'; |
|
||||
|
|
||||
import { wgInterface } from '../../schema'; |
|
||||
|
|
||||
export const prometheus = sqliteTable('prometheus_table', { |
|
||||
id: text() |
|
||||
.primaryKey() |
|
||||
.references(() => wgInterface.name, { |
|
||||
onDelete: 'cascade', |
|
||||
onUpdate: 'cascade', |
|
||||
}), |
|
||||
password: text().notNull(), |
|
||||
createdAt: text('created_at') |
|
||||
.notNull() |
|
||||
.default(sql`(CURRENT_TIMESTAMP)`), |
|
||||
updatedAt: text('updated_at') |
|
||||
.notNull() |
|
||||
.default(sql`(CURRENT_TIMESTAMP)`) |
|
||||
.$onUpdate(() => sql`(CURRENT_TIMESTAMP)`), |
|
||||
}); |
|
@ -1,31 +0,0 @@ |
|||||
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); |
|
||||
} |
|
||||
} |
|
@ -1,4 +0,0 @@ |
|||||
import type { InferSelectModel } from 'drizzle-orm'; |
|
||||
import type { prometheus } from './schema'; |
|
||||
|
|
||||
export type PrometheusType = InferSelectModel<typeof prometheus>; |
|
@ -1,14 +0,0 @@ |
|||||
export default defineEventHandler(async (event) => { |
|
||||
// TODO: check password
|
|
||||
|
|
||||
const prometheus = await Database.metrics.prometheus.get('wg0'); |
|
||||
if (!prometheus) { |
|
||||
throw createError({ |
|
||||
statusCode: 400, |
|
||||
message: 'Prometheus metrics are not enabled', |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
setHeader(event, 'Content-Type', 'text/plain'); |
|
||||
return getPrometheusResponse(); |
|
||||
}); |
|
@ -1,13 +1,35 @@ |
|||||
export default defineEventHandler(async () => { |
export default defineMetricsHandler('json', async () => { |
||||
// TODO: check password
|
|
||||
|
|
||||
const prometheus = await Database.metrics.prometheus.get('wg0'); |
|
||||
if (!prometheus) { |
|
||||
throw createError({ |
|
||||
statusCode: 400, |
|
||||
message: 'Prometheus metrics are not enabled', |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
return getMetricsJSON(); |
return getMetricsJSON(); |
||||
}); |
}); |
||||
|
|
||||
|
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 (isPeerConnected(client)) { |
||||
|
wireguardConnectedPeersCount++; |
||||
|
} |
||||
|
} |
||||
|
return { |
||||
|
wireguard_configured_peers: wireguardPeerCount, |
||||
|
wireguard_enabled_peers: wireguardEnabledPeersCount, |
||||
|
wireguard_connected_peers: wireguardConnectedPeersCount, |
||||
|
clients: clients.map((client) => ({ |
||||
|
name: client.name, |
||||
|
enabled: client.enabled, |
||||
|
ipv4Address: client.ipv4Address, |
||||
|
ipv6Address: client.ipv6Address, |
||||
|
publicKey: client.publicKey, |
||||
|
endpoint: client.endpoint, |
||||
|
latestHandshakeAt: client.latestHandshakeAt, |
||||
|
transferRx: client.transferRx, |
||||
|
transferTx: client.transferTx, |
||||
|
})), |
||||
|
}; |
||||
|
} |
||||
|
@ -0,0 +1,67 @@ |
|||||
|
export default defineMetricsHandler('prometheus', async ({ event }) => { |
||||
|
setHeader(event, 'Content-Type', 'text/plain'); |
||||
|
return getPrometheusResponse(); |
||||
|
}); |
||||
|
|
||||
|
async function getPrometheusResponse() { |
||||
|
const clients = await WireGuard.getClients(); |
||||
|
let wireguardPeerCount = 0; |
||||
|
let wireguardEnabledPeersCount = 0; |
||||
|
let wireguardConnectedPeersCount = 0; |
||||
|
const wireguardSentBytes = []; |
||||
|
const wireguardReceivedBytes = []; |
||||
|
const wireguardLatestHandshakeSeconds = []; |
||||
|
for (const client of clients) { |
||||
|
wireguardPeerCount++; |
||||
|
if (client.enabled === true) { |
||||
|
wireguardEnabledPeersCount++; |
||||
|
} |
||||
|
|
||||
|
if (isPeerConnected(client)) { |
||||
|
wireguardConnectedPeersCount++; |
||||
|
} |
||||
|
|
||||
|
const id = `interface="wg0",enabled="${client.enabled}",ipv4Address="${client.ipv4Address}",ipv6Address="${client.ipv6Address}",name="${client.name}"`; |
||||
|
|
||||
|
wireguardSentBytes.push( |
||||
|
`wireguard_sent_bytes{${id}} ${client.transferTx ?? 0}` |
||||
|
); |
||||
|
wireguardReceivedBytes.push( |
||||
|
`wireguard_received_bytes{${id}} ${client.transferRx ?? 0}` |
||||
|
); |
||||
|
// TODO: if latestHandshakeAt is null this would result in client showing as online?
|
||||
|
wireguardLatestHandshakeSeconds.push( |
||||
|
`wireguard_latest_handshake_seconds{${id}} ${client.latestHandshakeAt ? (Date.now() - client.latestHandshakeAt.getTime()) / 1000 : 0}` |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
const returnText = [ |
||||
|
'# HELP wg-easy and wireguard metrics', |
||||
|
'', |
||||
|
'# HELP wireguard_configured_peers', |
||||
|
'# TYPE wireguard_configured_peers gauge', |
||||
|
`wireguard_configured_peers{interface="wg0"} ${wireguardPeerCount}`, |
||||
|
'', |
||||
|
'# HELP wireguard_enabled_peers', |
||||
|
'# TYPE wireguard_enabled_peers gauge', |
||||
|
`wireguard_enabled_peers{interface="wg0"} ${wireguardEnabledPeersCount}`, |
||||
|
'', |
||||
|
'# HELP wireguard_connected_peers', |
||||
|
'# TYPE wireguard_connected_peers gauge', |
||||
|
`wireguard_connected_peers{interface="wg0"} ${wireguardConnectedPeersCount}`, |
||||
|
'', |
||||
|
'# HELP wireguard_sent_bytes Bytes sent to the peer', |
||||
|
'# TYPE wireguard_sent_bytes counter', |
||||
|
`${wireguardSentBytes.join('\n')}`, |
||||
|
'', |
||||
|
'# HELP wireguard_received_bytes Bytes received from the peer', |
||||
|
'# TYPE wireguard_received_bytes counter', |
||||
|
`${wireguardReceivedBytes.join('\n')}`, |
||||
|
'', |
||||
|
'# HELP wireguard_latest_handshake_seconds UNIX timestamp seconds of the last handshake', |
||||
|
'# TYPE wireguard_latest_handshake_seconds gauge', |
||||
|
`${wireguardLatestHandshakeSeconds.join('\n')}`, |
||||
|
]; |
||||
|
|
||||
|
return returnText.join('\n'); |
||||
|
} |
@ -1,74 +0,0 @@ |
|||||
// 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, |
|
||||
}; |
|
||||
} |
|
@ -0,0 +1,10 @@ |
|||||
|
export function isPeerConnected(client: { latestHandshakeAt: Date | null }) { |
||||
|
if (!client.latestHandshakeAt) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
const lastHandshakeMs = Date.now() - client.latestHandshakeAt.getTime(); |
||||
|
|
||||
|
// connected if last handshake was less than 10 minutes ago
|
||||
|
return lastHandshakeMs < 1000 * 60 * 10; |
||||
|
} |
Loading…
Reference in new issue