new clients page

This commit is contained in:
Yuri Tatishchev 2024-12-22 02:39:15 -08:00
parent bdea663178
commit 5015246a24
Signed by: CaZzzer
GPG Key ID: E0EBF441EA424369
7 changed files with 129 additions and 8 deletions

View File

@ -67,3 +67,41 @@ export interface OpnsenseWgServers {
uuid: string;
}[];
}
/**
* Sample response for OPNsense WireGuard clients
* ```json
* {
* "rows": [
* {
* "uuid": "d99334de-7671-4ca7-9c9b-5f5578acae70",
* "enabled": "1",
* "name": "Yura-TPX13",
* "pubkey": "iJa5JmJbMHNlbEluNwoB2Q8LyrPAfb7S/mluanMcI08=",
* "tunneladdress": "fd00::1/112,10.6.0.3/32",
* "serveraddress": "",
* "serverport": "",
* "servers": "wg0"
* }
* ],
* "rowCount": 1,
* "total": 10,
* "current": 1
* }
* ```
*/
export interface OpnsenseWgClients {
rowCount: number;
total: number;
current: number;
rows: {
uuid: string;
enabled: string;
name: string;
pubkey: string;
tunneladdress: string;
serveraddress: string;
serverport: string;
servers: string;
}[];
}

View File

@ -49,7 +49,14 @@ export const wgClients = sqliteTable('wg_clients', {
});
export const wgClientsRelations = relations(wgClients, ({ one }) => ({
ipAllocation: one(ipAllocations),
user: one(users, {
fields: [wgClients.userId],
references: [users.id],
}),
ipAllocation: one(ipAllocations, {
fields: [wgClients.id],
references: [ipAllocations.clientId],
}),
}));
export type WgClient = typeof wgClients.$inferSelect;

View File

@ -1,4 +1,4 @@
import { users, wgClients } from './schema';
import { ipAllocations, users, wgClients } from './schema';
import { eq } from 'drizzle-orm';
import assert from 'node:assert';
import { drizzle } from 'drizzle-orm/libsql';
@ -7,23 +7,27 @@ import * as schema from '$lib/server/db/schema';
assert(process.env.DATABASE_URL, 'DATABASE_URL is not set');
const db = drizzle(process.env.DATABASE_URL, { schema });
export async function seed() {
async function seed() {
const user = await db.query.users.findFirst({ where: eq(users.username, 'CaZzzer') });
assert(user, 'User not found');
const clients = [
const clients: typeof wgClients.$inferInsert[] = [
{
userId: user.id,
name: 'Client1',
publicKey: 'BJ5faPVJsDP4CCxNYilmKnwlQXOtXEOJjqIwb4U/CgM=',
privateKey: 'KKqsHDu30WCSrVsyzMkOKbE3saQ+wlx0sBwGs61UGXk=',
preSharedKey: '0LWopbrISXBNHUxr+WOhCSAg+0hD8j3TLmpyzHkBHCQ=',
ipIndex: 1,
// ipIndex: 1,
// allowedIps: '10.18.11.101/32,fd00::1/112',
}
]
},
];
const returned = await db.insert(wgClients).values(clients).returning({ insertedId: wgClients.id });
await db.insert(wgClients).values(clients);
const ipAllocation: typeof ipAllocations.$inferInsert = {
clientId: returned[0].insertedId,
};
await db.insert(ipAllocations).values(ipAllocation);
}
seed();

View File

@ -19,6 +19,7 @@
{#if user}
<a href="/user" class={getNavClass("/user")}>Profile</a>
<a href="/connections" class={getNavClass("/connections")}>Connections</a>
<a href="/clients" class={getNavClass("/clients")}>Clients</a>
{/if}
</nav>
</header>

View File

@ -0,0 +1,28 @@
import { error, type RequestHandler } from '@sveltejs/kit';
import { wgClients } from '$lib/server/db/schema';
import { db } from '$lib/server/db';
import { eq } from 'drizzle-orm';
export const GET: RequestHandler = async (event) => {
if (!event.locals.user) {
return error(401, 'Unauthorized');
}
const clients = await findClients(event.locals.user.id);
return new Response(
JSON.stringify({
clients,
})
);
};
async function findClients(userId: string) {
return db.query.wgClients.findMany({
where: eq(wgClients.userId, userId),
with: {
ipAllocation: true
}
});
}
export type Clients = Awaited<ReturnType<typeof findClients>>;

View File

@ -0,0 +1,34 @@
<script lang="ts">
import * as Table from '$lib/components/ui/table';
import { Badge } from '$lib/components/ui/badge';
import type { PageData } from './$types';
const { data }: { data: PageData } = $props();
</script>
<svelte:head>
<title>Clients</title>
</svelte:head>
<Table.Root class="bg-accent rounded-xl">
<Table.Header>
<Table.Head>Name</Table.Head>
<Table.Head>Public Key</Table.Head>
<Table.Head>Private Key</Table.Head>
<Table.Head>Pre-Shared Key</Table.Head>
<Table.Head>IP Allocation</Table.Head>
</Table.Header>
<Table.Body>
{#each data.clients as client}
<Table.Row class="border-y-2 border-background">
<Table.Cell>{client.name}</Table.Cell>
<Table.Cell class="truncate max-w-[10ch]">{client.publicKey}</Table.Cell>
<Table.Cell class="truncate max-w-[10ch]">{client.privateKey}</Table.Cell>
<Table.Cell class="truncate max-w-[10ch]">{client.preSharedKey}</Table.Cell>
<Table.Cell>
<Badge class="bg-background" variant="secondary">{client.ipAllocation.id}</Badge>
</Table.Cell>
</Table.Row>
{/each}
</Table.Body>
</Table.Root>

View File

@ -0,0 +1,9 @@
import type { PageLoad } from './$types';
import type { Clients } from '../api/clients/+server';
export const load: PageLoad = async ({ fetch }) => {
const res = await fetch('/api/clients');
const { clients } = await res.json() as { clients: Clients };
return { clients };
};