From cc7c94417d6bfc6280a4fb5db822e2a5cfd7b5b7 Mon Sep 17 00:00:00 2001 From: Yuri Tatishchev Date: Tue, 7 Jan 2025 16:20:40 -0800 Subject: [PATCH] rename clients to devices --- ...young_wong.sql => 0000_fair_tarantula.sql} | 33 ++-- drizzle/meta/0000_snapshot.json | 182 +++++++++--------- drizzle/meta/_journal.json | 4 +- src/hooks.server.ts | 2 +- .../{guide-client.mp4 => guide-android.mp4} | 0 src/lib/clients.ts | 33 ---- .../wireguard-guide/wireguard-guide.svelte | 4 +- src/lib/devices.ts | 43 +++++ src/lib/server/db/schema.ts | 24 +-- src/lib/server/db/seed.ts | 10 +- src/lib/server/{clients.ts => devices.ts} | 76 ++++---- src/lib/types/clients.ts | 11 -- src/lib/types/device.ts | 0 src/routes/+layout.svelte | 2 +- src/routes/+page.svelte | 2 +- src/routes/api/clients/[id]/+server.ts | 20 -- .../api/{clients => devices}/+server.ts | 10 +- src/routes/api/devices/[id]/+server.ts | 20 ++ src/routes/clients/+page.ts | 9 - src/routes/clients/[id]/+page.ts | 16 -- .../{clients => devices}/+page.server.ts | 6 +- src/routes/{clients => devices}/+page.svelte | 26 +-- src/routes/devices/+page.ts | 9 + .../{clients => devices}/[id]/+page.svelte | 14 +- src/routes/devices/[id]/+page.ts | 15 ++ 25 files changed, 284 insertions(+), 287 deletions(-) rename drizzle/{0000_young_wong.sql => 0000_fair_tarantula.sql} (67%) rename src/lib/assets/{guide-client.mp4 => guide-android.mp4} (100%) delete mode 100644 src/lib/clients.ts create mode 100644 src/lib/devices.ts rename src/lib/server/{clients.ts => devices.ts} (75%) delete mode 100644 src/lib/types/clients.ts create mode 100644 src/lib/types/device.ts delete mode 100644 src/routes/api/clients/[id]/+server.ts rename src/routes/api/{clients => devices}/+server.ts (70%) create mode 100644 src/routes/api/devices/[id]/+server.ts delete mode 100644 src/routes/clients/+page.ts delete mode 100644 src/routes/clients/[id]/+page.ts rename src/routes/{clients => devices}/+page.server.ts (81%) rename src/routes/{clients => devices}/+page.svelte (82%) create mode 100644 src/routes/devices/+page.ts rename src/routes/{clients => devices}/[id]/+page.svelte (71%) create mode 100644 src/routes/devices/[id]/+page.ts diff --git a/drizzle/0000_young_wong.sql b/drizzle/0000_fair_tarantula.sql similarity index 67% rename from drizzle/0000_young_wong.sql rename to drizzle/0000_fair_tarantula.sql index 542fecb..3218b10 100644 --- a/drizzle/0000_young_wong.sql +++ b/drizzle/0000_fair_tarantula.sql @@ -1,10 +1,22 @@ -CREATE TABLE `ip_allocations` ( +CREATE TABLE `devices` ( `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - `client_id` integer, - FOREIGN KEY (`client_id`) REFERENCES `wg_clients`(`id`) ON UPDATE no action ON DELETE set null + `user_id` text NOT NULL, + `name` text NOT NULL, + `opnsense_id` text, + `public_key` text NOT NULL, + `private_key` text, + `pre_shared_key` text, + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action ); --> statement-breakpoint -CREATE UNIQUE INDEX `ip_allocations_client_id_unique` ON `ip_allocations` (`client_id`);--> statement-breakpoint +CREATE UNIQUE INDEX `devices_public_key_unique` ON `devices` (`public_key`);--> statement-breakpoint +CREATE TABLE `ip_allocations` ( + `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, + `device_id` integer, + FOREIGN KEY (`device_id`) REFERENCES `devices`(`id`) ON UPDATE no action ON DELETE set null +); +--> statement-breakpoint +CREATE UNIQUE INDEX `ip_allocations_device_id_unique` ON `ip_allocations` (`device_id`);--> statement-breakpoint CREATE TABLE `sessions` ( `id` text PRIMARY KEY NOT NULL, `user_id` text NOT NULL, @@ -17,16 +29,3 @@ CREATE TABLE `users` ( `username` text NOT NULL, `name` text NOT NULL ); ---> statement-breakpoint -CREATE TABLE `wg_clients` ( - `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - `user_id` text NOT NULL, - `name` text NOT NULL, - `opnsense_id` text, - `public_key` text NOT NULL, - `private_key` text, - `pre_shared_key` text, - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE UNIQUE INDEX `wg_clients_public_key_unique` ON `wg_clients` (`public_key`); \ No newline at end of file diff --git a/drizzle/meta/0000_snapshot.json b/drizzle/meta/0000_snapshot.json index dbdb533..738a5db 100644 --- a/drizzle/meta/0000_snapshot.json +++ b/drizzle/meta/0000_snapshot.json @@ -1,9 +1,90 @@ { "version": "6", "dialect": "sqlite", - "id": "29e6fd88-fa47-4f79-ad83-c52538bc36a6", + "id": "48b7ce55-58f1-4b97-a144-ca733576dba6", "prevId": "00000000-0000-0000-0000-000000000000", "tables": { + "devices": { + "name": "devices", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "opnsense_id": { + "name": "opnsense_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "public_key": { + "name": "public_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "private_key": { + "name": "private_key", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "pre_shared_key": { + "name": "pre_shared_key", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "devices_public_key_unique": { + "name": "devices_public_key_unique", + "columns": [ + "public_key" + ], + "isUnique": true + } + }, + "foreignKeys": { + "devices_user_id_users_id_fk": { + "name": "devices_user_id_users_id_fk", + "tableFrom": "devices", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, "ip_allocations": { "name": "ip_allocations", "columns": { @@ -14,8 +95,8 @@ "notNull": true, "autoincrement": true }, - "client_id": { - "name": "client_id", + "device_id": { + "name": "device_id", "type": "integer", "primaryKey": false, "notNull": false, @@ -23,21 +104,21 @@ } }, "indexes": { - "ip_allocations_client_id_unique": { - "name": "ip_allocations_client_id_unique", + "ip_allocations_device_id_unique": { + "name": "ip_allocations_device_id_unique", "columns": [ - "client_id" + "device_id" ], "isUnique": true } }, "foreignKeys": { - "ip_allocations_client_id_wg_clients_id_fk": { - "name": "ip_allocations_client_id_wg_clients_id_fk", + "ip_allocations_device_id_devices_id_fk": { + "name": "ip_allocations_device_id_devices_id_fk", "tableFrom": "ip_allocations", - "tableTo": "wg_clients", + "tableTo": "devices", "columnsFrom": [ - "client_id" + "device_id" ], "columnsTo": [ "id" @@ -125,87 +206,6 @@ "compositePrimaryKeys": {}, "uniqueConstraints": {}, "checkConstraints": {} - }, - "wg_clients": { - "name": "wg_clients", - "columns": { - "id": { - "name": "id", - "type": "integer", - "primaryKey": true, - "notNull": true, - "autoincrement": true - }, - "user_id": { - "name": "user_id", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "opnsense_id": { - "name": "opnsense_id", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "public_key": { - "name": "public_key", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "private_key": { - "name": "private_key", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "pre_shared_key": { - "name": "pre_shared_key", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - } - }, - "indexes": { - "wg_clients_public_key_unique": { - "name": "wg_clients_public_key_unique", - "columns": [ - "public_key" - ], - "isUnique": true - } - }, - "foreignKeys": { - "wg_clients_user_id_users_id_fk": { - "name": "wg_clients_user_id_users_id_fk", - "tableFrom": "wg_clients", - "tableTo": "users", - "columnsFrom": [ - "user_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "checkConstraints": {} } }, "views": {}, diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index 572f703..d0690c3 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -5,8 +5,8 @@ { "idx": 0, "version": "6", - "when": 1735028333867, - "tag": "0000_young_wong", + "when": 1736295566569, + "tag": "0000_fair_tarantula", "breakpoints": true } ] diff --git a/src/hooks.server.ts b/src/hooks.server.ts index ae1ad34..f523dc5 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -39,7 +39,7 @@ const authRequired = [ /^\/api/, /^\/user/, /^\/connections/, - /^\/clients/, + /^\/devices/, ]; const handleProtectedPaths: Handle = ({ event, resolve }) => { const isProtected = authRequired.some((re) => re.test(event.url.pathname)); diff --git a/src/lib/assets/guide-client.mp4 b/src/lib/assets/guide-android.mp4 similarity index 100% rename from src/lib/assets/guide-client.mp4 rename to src/lib/assets/guide-android.mp4 diff --git a/src/lib/clients.ts b/src/lib/clients.ts deleted file mode 100644 index 91af124..0000000 --- a/src/lib/clients.ts +++ /dev/null @@ -1,33 +0,0 @@ -import type { ClientDetails } from '$lib/types/clients'; - -/** - * Convert client details to WireGuard configuration. - * - * ```conf - * [Interface] - * PrivateKey = wPa07zR0H4wYoc1ljfeiqlSbR8Z28pPc6jplwE7zPms= - * Address = 10.18.11.100/32,fd00::1/128 - * DNS = 10.18.11.1,fd00::0 - * - * [Peer] - * PublicKey = BJ5faPVJsDP4CCxNYilmKnwlQXOtXEOJjqIwb4U/CgM= - * PresharedKey = uhZUVqXKF0oayP0BS6yPu6Gepgh68Nz9prtbE5Cuok0= - * Endpoint = vpn.lab.cazzzer.com:51820 - * AllowedIPs = 0.0.0.0/0,::/0 - * ``` - * @param client - */ -export function clientDetailsToConfig(client: ClientDetails): string { - return `\ -[Interface] -PrivateKey = ${client.privateKey} -Address = ${client.ips.join(', ')} -DNS = ${client.vpnDns} - -[Peer] -PublicKey = ${client.vpnPublicKey} -PresharedKey = ${client.preSharedKey} -Endpoint = ${client.vpnEndpoint} -AllowedIPs = 0.0.0.0/0,::/0 -`; -} diff --git a/src/lib/components/app/wireguard-guide/wireguard-guide.svelte b/src/lib/components/app/wireguard-guide/wireguard-guide.svelte index f390c02..0d2de92 100644 --- a/src/lib/components/app/wireguard-guide/wireguard-guide.svelte +++ b/src/lib/components/app/wireguard-guide/wireguard-guide.svelte @@ -2,7 +2,7 @@ import * as Tabs from '$lib/components/ui/tabs'; import * as Card from '$lib/components/ui/card'; import getItOnGooglePlay from '$lib/assets/GetItOnGooglePlay_Badge_Web_color_English.png'; - import guideVideo from '$lib/assets/guide-client.mp4'; + import guideVideoAndroid from '$lib/assets/guide-android.mp4'; @@ -36,7 +36,7 @@

Download the configuration file and import it

diff --git a/src/lib/devices.ts b/src/lib/devices.ts new file mode 100644 index 0000000..52e1390 --- /dev/null +++ b/src/lib/devices.ts @@ -0,0 +1,43 @@ +/** + * Convert device details to WireGuard configuration. + * + * ```conf + * [Interface] + * PrivateKey = wPa07zR0H4wYoc1ljfeiqlSbR8Z28pPc6jplwE7zPms= + * Address = 10.18.11.100/32,fd00::1/128 + * DNS = 10.18.11.1,fd00::0 + * + * [Peer] + * PublicKey = BJ5faPVJsDP4CCxNYilmKnwlQXOtXEOJjqIwb4U/CgM= + * PresharedKey = uhZUVqXKF0oayP0BS6yPu6Gepgh68Nz9prtbE5Cuok0= + * Endpoint = vpn.lab.cazzzer.com:51820 + * AllowedIPs = 0.0.0.0/0,::/0 + * ``` + * @param device + */ +export function deviceDetailsToConfig(device: DeviceDetails): string { + return `\ +[Interface] +PrivateKey = ${device.privateKey} +Address = ${device.ips.join(', ')} +DNS = ${device.vpnDns} + +[Peer] +PublicKey = ${device.vpnPublicKey} +PresharedKey = ${device.preSharedKey} +Endpoint = ${device.vpnEndpoint} +AllowedIPs = 0.0.0.0/0,::/0 +`; +} + +export type DeviceDetails = { + id: number; + name: string; + publicKey: string; + privateKey: string | null; + preSharedKey: string | null; + ips: string[]; + vpnPublicKey: string; + vpnEndpoint: string; + vpnDns: string; +}; diff --git a/src/lib/server/db/schema.ts b/src/lib/server/db/schema.ts index a806c2d..2a9595f 100644 --- a/src/lib/server/db/schema.ts +++ b/src/lib/server/db/schema.ts @@ -8,7 +8,7 @@ export const users = sqliteTable('users', { }); export const usersRelations = relations(users, ({ many }) => ({ - wgClients: many(wgClients), + devices: many(devices), })); export const sessions = sqliteTable('sessions', { @@ -22,14 +22,14 @@ export const sessions = sqliteTable('sessions', { export const ipAllocations = sqliteTable('ip_allocations', { // for now, id will be the same as the ipIndex id: integer('id').primaryKey({ autoIncrement: true }), - // clientId is nullable because allocations can remain after the client is deleted - // unique for now, only allowing one allocation per client - clientId: integer('client_id') + // deviceId is nullable because allocations can remain after the device is deleted + // unique for now, only allowing one allocation per device + deviceId: integer('device_id') .unique() - .references(() => wgClients.id, { onDelete: 'set null' }), + .references(() => devices.id, { onDelete: 'set null' }), }); -export const wgClients = sqliteTable('wg_clients', { +export const devices = sqliteTable('devices', { id: integer().primaryKey({ autoIncrement: true }), userId: text('user_id') .notNull() @@ -38,7 +38,7 @@ export const wgClients = sqliteTable('wg_clients', { // questioning whether this should be nullable opnsenseId: text('opnsense_id'), publicKey: text('public_key').notNull().unique(), - // nullable for the possibility of a client supplying their own private key + // nullable for the possibility of a user supplying their own private key privateKey: text('private_key'), // nullable for the possibility of no psk preSharedKey: text('pre_shared_key'), @@ -48,18 +48,18 @@ export const wgClients = sqliteTable('wg_clients', { // allowedIps: text('allowed_ips').notNull(), }); -export const wgClientsRelations = relations(wgClients, ({ one }) => ({ +export const devicesRelations = relations(devices, ({ one }) => ({ user: one(users, { - fields: [wgClients.userId], + fields: [devices.userId], references: [users.id], }), ipAllocation: one(ipAllocations, { - fields: [wgClients.id], - references: [ipAllocations.clientId], + fields: [devices.id], + references: [ipAllocations.deviceId], }), })); -export type WgClient = typeof wgClients.$inferSelect; +export type Device = typeof devices.$inferSelect; export type Session = typeof sessions.$inferSelect; diff --git a/src/lib/server/db/seed.ts b/src/lib/server/db/seed.ts index 1ebb61e..5e5e80e 100644 --- a/src/lib/server/db/seed.ts +++ b/src/lib/server/db/seed.ts @@ -1,4 +1,4 @@ -import { ipAllocations, users, wgClients } from './schema'; +import { ipAllocations, users, devices } from './schema'; import { eq } from 'drizzle-orm'; import assert from 'node:assert'; import { drizzle } from 'drizzle-orm/libsql'; @@ -11,10 +11,10 @@ async function seed() { const user = await db.query.users.findFirst({ where: eq(users.username, 'CaZzzer') }); assert(user, 'User not found'); - const clients: typeof wgClients.$inferInsert[] = [ + const newDevices: typeof devices.$inferInsert[] = [ { userId: user.id, - name: 'Client1', + name: 'Device1', publicKey: 'BJ5faPVJsDP4CCxNYilmKnwlQXOtXEOJjqIwb4U/CgM=', privateKey: 'KKqsHDu30WCSrVsyzMkOKbE3saQ+wlx0sBwGs61UGXk=', preSharedKey: '0LWopbrISXBNHUxr+WOhCSAg+0hD8j3TLmpyzHkBHCQ=', @@ -22,10 +22,10 @@ async function seed() { // allowedIps: '10.18.11.101/32,fd00::1/112', }, ]; - const returned = await db.insert(wgClients).values(clients).returning({ insertedId: wgClients.id }); + const returned = await db.insert(devices).values(newDevices).returning({ insertedId: devices.id }); const ipAllocation: typeof ipAllocations.$inferInsert = { - clientId: returned[0].insertedId, + deviceId: returned[0].insertedId, }; await db.insert(ipAllocations).values(ipAllocation); } diff --git a/src/lib/server/clients.ts b/src/lib/server/devices.ts similarity index 75% rename from src/lib/server/clients.ts rename to src/lib/server/devices.ts index 5178bc0..eda67e9 100644 --- a/src/lib/server/clients.ts +++ b/src/lib/server/devices.ts @@ -1,15 +1,15 @@ import type { User } from '$lib/server/db/schema'; -import { ipAllocations, wgClients } from '$lib/server/db/schema'; +import { ipAllocations, devices } from '$lib/server/db/schema'; import { db } from '$lib/server/db'; import { opnsenseAuth, opnsenseUrl, serverPublicKey, serverUuid } from '$lib/server/opnsense'; import { Address4, Address6 } from 'ip-address'; import { env } from '$env/dynamic/private'; import { and, count, eq, isNull } from 'drizzle-orm'; import { err, ok, type Result } from '$lib/types'; -import type { ClientDetails } from '$lib/types/clients'; +import type { DeviceDetails } from '$lib/devices'; -export async function findClients(userId: string) { - return db.query.wgClients.findMany({ +export async function findDevices(userId: string) { + return db.query.devices.findMany({ columns: { id: true, name: true, @@ -20,12 +20,12 @@ export async function findClients(userId: string) { with: { ipAllocation: true, }, - where: eq(wgClients.userId, userId), + where: eq(devices.userId, userId), }); } -export async function findClient(userId: string, clientId: number) { - return db.query.wgClients.findFirst({ +export async function findDevice(userId: string, deviceId: number) { + return db.query.devices.findFirst({ columns: { id: true, name: true, @@ -36,20 +36,20 @@ export async function findClient(userId: string, clientId: number) { with: { ipAllocation: true, }, - where: and(eq(wgClients.userId, userId), eq(wgClients.id, clientId)), + where: and(eq(devices.userId, userId), eq(devices.id, deviceId)), }); } -export function mapClientToDetails( - client: Awaited>[0], -): ClientDetails { - const ips = getIpsFromIndex(client.ipAllocation.id); +export function mapDeviceToDetails( + device: Awaited>[0], +): DeviceDetails { + const ips = getIpsFromIndex(device.ipAllocation.id); return { - id: client.id, - name: client.name, - publicKey: client.publicKey, - privateKey: client.privateKey, - preSharedKey: client.preSharedKey, + id: device.id, + name: device.name, + publicKey: device.publicKey, + privateKey: device.privateKey, + preSharedKey: device.preSharedKey, ips, vpnPublicKey: serverPublicKey, vpnEndpoint: env.VPN_ENDPOINT, @@ -57,35 +57,35 @@ export function mapClientToDetails( }; } -export async function createClient(params: { +export async function createDevice(params: { name: string; user: User; }): Promise> { - // check if user exceeds the limit of clients - const [{ clientCount }] = await db - .select({ clientCount: count() }) - .from(wgClients) - .where(eq(wgClients.userId, params.user.id)); - if (clientCount >= parseInt(env.MAX_CLIENTS_PER_USER)) - return err([400, 'Maximum number of clients reached'] as [400, string]); + // check if user exceeds the limit of devices + const [{ deviceCount }] = await db + .select({ deviceCount: count() }) + .from(devices) + .where(eq(devices.userId, params.user.id)); + if (deviceCount >= parseInt(env.MAX_CLIENTS_PER_USER)) + return err([400, 'Maximum number of devices reached'] as [400, string]); // this is going to be quite long - // 1. fetch params for new client from opnsense api - // 2.1 get an allocation for the client - // 2.2. insert new client into db - // 2.3. update the allocation with the client id + // 1. fetch params for new device from opnsense api + // 2.1 get an allocation for the device + // 2.2. insert new device into db + // 2.3. update the allocation with the device id // 3. create the client in opnsense // 4. reconfigure opnsense to enable the new client return await db.transaction(async (tx) => { const [keys, availableAllocation, lastAllocation] = await Promise.all([ - // fetch params for new client from opnsense api + // fetch params for new device from opnsense api getKeys(), // find first unallocated IP await tx.query.ipAllocations.findFirst({ columns: { id: true, }, - where: isNull(ipAllocations.clientId), + where: isNull(ipAllocations.deviceId), }), // find last allocation to check if we have any IPs left await tx.query.ipAllocations.findFirst({ @@ -109,9 +109,9 @@ export async function createClient(params: { // transaction savepoint after creating a new IP allocation // TODO: not sure if this is needed return await tx.transaction(async (tx2) => { - // create new client in db - const [newClient] = await tx2 - .insert(wgClients) + // create new device in db + const [newDevice] = await tx2 + .insert(devices) .values({ userId: params.user.id, name: params.name, @@ -119,12 +119,12 @@ export async function createClient(params: { privateKey: keys.privkey, preSharedKey: keys.psk, }) - .returning({ id: wgClients.id }); + .returning({ id: devices.id }); - // update IP allocation with client ID + // update IP allocation with device ID await tx2 .update(ipAllocations) - .set({ clientId: newClient.id }) + .set({ deviceId: newDevice.id }) .where(eq(ipAllocations.id, ipAllocationId)); // create client in opnsense @@ -143,7 +143,7 @@ export async function createClient(params: { // reconfigure opnsense await opnsenseReconfigure(); - return ok(newClient.id); + return ok(newDevice.id); }); }); } diff --git a/src/lib/types/clients.ts b/src/lib/types/clients.ts deleted file mode 100644 index 210dfef..0000000 --- a/src/lib/types/clients.ts +++ /dev/null @@ -1,11 +0,0 @@ -export type ClientDetails = { - id: number; - name: string; - publicKey: string; - privateKey: string | null; - preSharedKey: string | null; - ips: string[]; - vpnPublicKey: string; - vpnEndpoint: string; - vpnDns: string; -}; diff --git a/src/lib/types/device.ts b/src/lib/types/device.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index a1f6331..b04b3da 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -24,7 +24,7 @@ {#if user}
  • Profile
  • Connections
  • -
  • Clients
  • +
  • Devices
  • {/if} diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index fc592d7..261c2c0 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -21,7 +21,7 @@

    To get started, - +

    diff --git a/src/routes/api/clients/[id]/+server.ts b/src/routes/api/clients/[id]/+server.ts deleted file mode 100644 index e144971..0000000 --- a/src/routes/api/clients/[id]/+server.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { error } from '@sveltejs/kit'; -import type { RequestHandler } from './$types'; -import { findClient, mapClientToDetails } from '$lib/server/clients'; - -export const GET: RequestHandler = async (event) => { - if (!event.locals.user) { - return error(401, 'Unauthorized'); - } - - const { id } = event.params; - const clientId = parseInt(id); - if (isNaN(clientId)) { - return error(400, 'Invalid client ID'); - } - const client = await findClient(event.locals.user.id, clientId); - if (!client) { - return error(404, 'Client not found'); - } - return new Response(JSON.stringify(mapClientToDetails(client))); -}; diff --git a/src/routes/api/clients/+server.ts b/src/routes/api/devices/+server.ts similarity index 70% rename from src/routes/api/clients/+server.ts rename to src/routes/api/devices/+server.ts index 3fa49f9..951caca 100644 --- a/src/routes/api/clients/+server.ts +++ b/src/routes/api/devices/+server.ts @@ -1,29 +1,29 @@ import { error } from '@sveltejs/kit'; import type { RequestHandler } from './$types'; -import { createClient, findClients, mapClientToDetails } from '$lib/server/clients'; +import { createDevice, findDevices, mapDeviceToDetails } from '$lib/server/devices'; export const GET: RequestHandler = async (event) => { if (!event.locals.user) { return error(401, 'Unauthorized'); } - const clients = await findClients(event.locals.user.id); + const devices = await findDevices(event.locals.user.id); return new Response( JSON.stringify({ - clients: clients.map(mapClientToDetails), + devices: devices.map(mapDeviceToDetails), }), ); }; -export type Clients = Awaited>; +export type Devices = Awaited>; export const POST: RequestHandler = async (event) => { if (!event.locals.user) { return error(401, 'Unauthorized'); } const { name } = await event.request.json(); - const res = await createClient({ + const res = await createDevice({ name, user: event.locals.user, }); diff --git a/src/routes/api/devices/[id]/+server.ts b/src/routes/api/devices/[id]/+server.ts new file mode 100644 index 0000000..969ddf8 --- /dev/null +++ b/src/routes/api/devices/[id]/+server.ts @@ -0,0 +1,20 @@ +import { error } from '@sveltejs/kit'; +import type { RequestHandler } from './$types'; +import { findDevice, mapDeviceToDetails } from '$lib/server/devices'; + +export const GET: RequestHandler = async (event) => { + if (!event.locals.user) { + return error(401, 'Unauthorized'); + } + + const { id } = event.params; + const deviceId = parseInt(id); + if (isNaN(deviceId)) { + return error(400, 'Invalid device ID'); + } + const device = await findDevice(event.locals.user.id, deviceId); + if (!device) { + return error(404, 'Device not found'); + } + return new Response(JSON.stringify(mapDeviceToDetails(device))); +}; diff --git a/src/routes/clients/+page.ts b/src/routes/clients/+page.ts deleted file mode 100644 index 520bb6e..0000000 --- a/src/routes/clients/+page.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { PageLoad } from './$types'; -import type { ClientDetails } from '$lib/types/clients'; - -export const load: PageLoad = async ({ fetch }) => { - const res = await fetch('/api/clients'); - const { clients } = await res.json() as { clients: ClientDetails[] }; - - return { clients }; -}; diff --git a/src/routes/clients/[id]/+page.ts b/src/routes/clients/[id]/+page.ts deleted file mode 100644 index bd27d3b..0000000 --- a/src/routes/clients/[id]/+page.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { PageLoad } from './$types'; -import type { ClientDetails } from '$lib/types/clients'; -import { clientDetailsToConfig } from '$lib/clients'; -import { error } from '@sveltejs/kit'; - -export const load: PageLoad = async ({ fetch, params }) => { - const res = await fetch(`/api/clients/${params.id}`); - const resJson = await res.json(); - if (!res.ok) { - return error(res.status, resJson['message']); - } - const client = resJson as ClientDetails; - const config = clientDetailsToConfig(client); - - return { client, config }; -}; diff --git a/src/routes/clients/+page.server.ts b/src/routes/devices/+page.server.ts similarity index 81% rename from src/routes/clients/+page.server.ts rename to src/routes/devices/+page.server.ts index 86fbfc5..44755de 100644 --- a/src/routes/clients/+page.server.ts +++ b/src/routes/devices/+page.server.ts @@ -1,5 +1,5 @@ import type { Actions } from './$types'; -import { createClient } from '$lib/server/clients'; +import { createDevice } from '$lib/server/devices'; import { error, redirect } from '@sveltejs/kit'; export const actions = { @@ -8,14 +8,14 @@ export const actions = { const formData = await event.request.formData(); const name = formData.get('name'); if (typeof name !== 'string' || name.trim() === '') return error(400, 'Invalid name'); - const res = await createClient({ + const res = await createDevice({ name: name.trim(), user: event.locals.user, }); switch (res._tag) { case 'ok': { - return redirect(303, `/clients/${res.value}`); + return redirect(303, `/devices/${res.value}`); } case 'err': { const [status, message] = res.error; diff --git a/src/routes/clients/+page.svelte b/src/routes/devices/+page.svelte similarity index 82% rename from src/routes/clients/+page.svelte rename to src/routes/devices/+page.svelte index 83f8b58..3181a90 100644 --- a/src/routes/clients/+page.svelte +++ b/src/routes/devices/+page.svelte @@ -23,7 +23,7 @@ - Clients + Devices @@ -37,21 +37,21 @@ - {#each data.clients as client} + {#each data.devices as device} - {client.name} + {device.name} - {client.publicKey} - - + {device.publicKey} + + - {#each client.ips as ip} + {#each device.ips as ip} {ip} {/each} @@ -60,18 +60,18 @@ - +
    - New Client + New Device
    - Create a new client + Add a new device
    @@ -81,12 +81,12 @@ pattern=".*[^\s]+.*" type="text" name="name" - placeholder="New Client" + placeholder="New Device" class="max-w-[20ch]" />
    - +
    diff --git a/src/routes/devices/+page.ts b/src/routes/devices/+page.ts new file mode 100644 index 0000000..1262f94 --- /dev/null +++ b/src/routes/devices/+page.ts @@ -0,0 +1,9 @@ +import type { PageLoad } from './$types'; +import type { DeviceDetails } from '$lib/devices'; + +export const load: PageLoad = async ({ fetch }) => { + const res = await fetch('/api/devices'); + const { devices } = await res.json() as { devices: DeviceDetails[] }; + + return { devices }; +}; diff --git a/src/routes/clients/[id]/+page.svelte b/src/routes/devices/[id]/+page.svelte similarity index 71% rename from src/routes/clients/[id]/+page.svelte rename to src/routes/devices/[id]/+page.svelte index 8a8c4f2..7705510 100644 --- a/src/routes/clients/[id]/+page.svelte +++ b/src/routes/devices/[id]/+page.svelte @@ -6,11 +6,11 @@ const { data }: { data: PageData } = $props(); - // Clean the client name for the file name, + // Clean the device name for the wg config filename, // things can break otherwise (too long or invalid characters) // https://github.com/pirate/wireguard-docs - const clientWgCleanedName = - data.client.name.slice(0, 15).replace(/[^a-zA-Z0-9_=+.-]/g, '_') + '.conf'; + const deviceWgCleanedName = + data.device.name.slice(0, 15).replace(/[^a-zA-Z0-9_=+.-]/g, '_') + '.conf'; let qrCode = new QRCode({ content: data.config, @@ -22,13 +22,13 @@ - {data.client.name} + {data.device.name} -

    {data.client.name}

    +

    {data.device.name}

    -
    - +
    +
    {@html qrCode.svg()} diff --git a/src/routes/devices/[id]/+page.ts b/src/routes/devices/[id]/+page.ts new file mode 100644 index 0000000..ca18e67 --- /dev/null +++ b/src/routes/devices/[id]/+page.ts @@ -0,0 +1,15 @@ +import type { PageLoad } from './$types'; +import { type DeviceDetails, deviceDetailsToConfig } from '$lib/devices'; +import { error } from '@sveltejs/kit'; + +export const load: PageLoad = async ({ fetch, params }) => { + const res = await fetch(`/api/devices/${params.id}`); + const resJson = await res.json(); + if (!res.ok) { + return error(res.status, resJson['message']); + } + const device = resJson as DeviceDetails; + const config = deviceDetailsToConfig(device); + + return { device, config }; +};