diff --git a/.env.example b/.env.example
index 59c7535..54809c2 100644
--- a/.env.example
+++ b/.env.example
@@ -3,3 +3,6 @@ AUTH_DOMAIN=auth.lab.cazzzer.com
AUTH_CLIENT_ID=
AUTH_CLIENT_SECRET=
AUTH_REDIRECT_URI=http://localhost:5173/auth/authentik/callback
+OPNSENSE_API_URL=https://opnsense.home
+OPNSENSE_API_KEY=
+OPNSENSE_API_SECRET=
diff --git a/bruno/bruno.json b/bruno/bruno.json
new file mode 100644
index 0000000..d9ce628
--- /dev/null
+++ b/bruno/bruno.json
@@ -0,0 +1,9 @@
+{
+ "version": "1",
+ "name": "vpgen",
+ "type": "collection",
+ "ignore": [
+ "node_modules",
+ ".git"
+ ]
+}
\ No newline at end of file
diff --git a/bruno/collection.bru b/bruno/collection.bru
new file mode 100644
index 0000000..d5edef6
--- /dev/null
+++ b/bruno/collection.bru
@@ -0,0 +1,8 @@
+auth {
+ mode: basic
+}
+
+auth:basic {
+ username: {{opnsense_key}}
+ password: {{opnsense_secret}}
+}
diff --git a/bruno/environments/dev.bru b/bruno/environments/dev.bru
new file mode 100644
index 0000000..ccf903c
--- /dev/null
+++ b/bruno/environments/dev.bru
@@ -0,0 +1,6 @@
+vars {
+ opnsense_key: 33NhXqaJwrWy1T4Qi60GK90RXJuS3PWIYwlwYPnQ8f5YPe/J1q/g6/l4bZ2/kJk71MFhwP+9mr+IiQPi
+}
+vars:secret [
+ opnsense_secret
+]
diff --git a/bruno/opnsense-api/Get Interfaces.bru b/bruno/opnsense-api/Get Interfaces.bru
new file mode 100644
index 0000000..a2780a3
--- /dev/null
+++ b/bruno/opnsense-api/Get Interfaces.bru
@@ -0,0 +1,28 @@
+meta {
+ name: Get Interfaces
+ type: http
+ seq: 2
+}
+
+post {
+ url: https://opnsense.home/api/wireguard/service/show
+ body: json
+ auth: inherit
+}
+
+headers {
+ Content-Type: application/json
+ Accept: application/json
+}
+
+body:json {
+ {
+ "current": 1,
+ "rowCount": 7,
+ "sort": {},
+ "searchPhrase": "",
+ "type": [
+ "interface"
+ ]
+ }
+}
diff --git a/bruno/opnsense-api/Get Peers.bru b/bruno/opnsense-api/Get Peers.bru
new file mode 100644
index 0000000..dbe024f
--- /dev/null
+++ b/bruno/opnsense-api/Get Peers.bru
@@ -0,0 +1,28 @@
+meta {
+ name: Get Peers
+ type: http
+ seq: 1
+}
+
+post {
+ url: https://opnsense.home/api/wireguard/service/show
+ body: json
+ auth: inherit
+}
+
+headers {
+ Content-Type: application/json
+ Accept: application/json
+}
+
+body:json {
+ {
+ "current": 1,
+ "rowCount": 7,
+ "sort": {},
+ "searchPhrase": "",
+ "type": [
+ "peer"
+ ]
+ }
+}
diff --git a/src/hooks.server.ts b/src/hooks.server.ts
index c04ae53..ae3130b 100644
--- a/src/hooks.server.ts
+++ b/src/hooks.server.ts
@@ -34,6 +34,7 @@ const handleAuth: Handle = async ({ event, resolve }) => {
const authRequired = new Set([
'/user',
'/connections',
+ '/api/connections',
]);
const handleProtectedPaths: Handle = ({ event, resolve }) => {
if (authRequired.has(event.url.pathname) && !event.locals.user) {
diff --git a/src/lib/opnsense/wg.ts b/src/lib/opnsense/wg.ts
new file mode 100644
index 0000000..59872fd
--- /dev/null
+++ b/src/lib/opnsense/wg.ts
@@ -0,0 +1,56 @@
+export interface PeerRow {
+ if: string;
+ type: "peer";
+ 'public-key': string;
+ endpoint: string;
+ 'allowed-ips': string;
+ 'latest-handshake': number;
+ 'transfer-rx': number;
+ 'transfer-tx': number;
+ 'persistent-keepalive': string;
+ name: string;
+ ifname: string;
+}
+
+export interface OpnsenseWgPeers {
+ total: number;
+ rowCount: number;
+ current: number;
+ rows: PeerRow[];
+}
+
+// Sample request to OPNsense WireGuard API
+// const url = 'https://opnsense.home/api/wireguard/service/show';
+// const options = {
+// method: 'POST',
+// headers: {
+// Authorization: 'Basic ...',
+// 'Content-Type': 'application/json',
+// Accept: 'application/json',
+// 'content-type': 'application/json'
+// },
+// body: '{"current":1,"rowCount":7,"sort":{},"searchPhrase":"","type":["peer"]}'
+// };
+
+
+// Sample response from OPNsense WireGuard API
+// {
+// "total": 17,
+// "rowCount": 7,
+// "current": 1,
+// "rows": [
+// {
+// "if": "wg0",
+// "type": "peer",
+// "public-key": "iJa5JmJbMHNlbEluNwoB2Q8LyrPAfb7S/mluanMcI08=",
+// "endpoint": "10.17.20.107:42516",
+// "allowed-ips": "fd00::1/112,10.6.0.3/32",
+// "latest-handshake": 1729319339,
+// "transfer-rx": 1052194743,
+// "transfer-tx": 25203263456,
+// "persistent-keepalive": "off",
+// "name": "Yura-TPX13",
+// "ifname": "wg0"
+// }
+// ]
+// }
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte
index 6a6b74c..8fbaf53 100644
--- a/src/routes/+layout.svelte
+++ b/src/routes/+layout.svelte
@@ -5,16 +5,20 @@
const { data, children } = $props();
const { user } = data;
+
+ function getNavClass(path: string) {
+ return cn("hover:text-foreground/80 transition-colors",
+ $page.url.pathname === path ? "text-foreground" : "text-foreground/60");
+ }
Public Key | +Endpoint | +Allowed IPs | +Latest Handshake | +RX | +TX | +Persistent Keepalive | +Name | +Interface Name | +|
---|---|---|---|---|---|---|---|---|---|
{peer['public-key'].substring(0, 10)} | +{peer.endpoint} | +{peer['allowed-ips']} | + {#if peer['latest-handshake']} +{new Date(peer['latest-handshake'] * 1000)} | + {:else} +Never | + {/if} +{getSize(peer['transfer-rx'])} | +{getSize(peer['transfer-tx'])} | +{peer['persistent-keepalive']} | +{peer.name} | +{peer.ifname} | +