From 56898142839671801e9e1ecc4433bb3b7a490f5c Mon Sep 17 00:00:00 2001 From: Bernd Storath <999999bst@gmail.com> Date: Wed, 12 Feb 2025 10:27:43 +0100 Subject: [PATCH] reorder setup, be able to migrate --- src/app/pages/setup/2.vue | 11 +-- src/app/pages/setup/3.vue | 75 ++++++++++++++++- src/app/pages/setup/4.vue | 72 +--------------- src/app/stores/setup.ts | 6 +- src/i18n/locales/en.json | 3 +- src/server/api/setup/{4.post.ts => 3.post.ts} | 2 +- src/server/api/setup/migrate.post.ts | 82 +++++++++---------- .../database/repositories/client/service.ts | 37 ++++++++- .../database/repositories/client/types.ts | 13 +++ .../repositories/oneTimeLink/types.ts | 4 +- src/server/utils/ip.ts | 4 +- src/server/utils/types.ts | 8 ++ 12 files changed, 183 insertions(+), 134 deletions(-) rename src/server/api/setup/{4.post.ts => 3.post.ts} (89%) diff --git a/src/app/pages/setup/2.vue b/src/app/pages/setup/2.vue index 9d11c381..6251264d 100644 --- a/src/app/pages/setup/2.vue +++ b/src/app/pages/setup/2.vue @@ -1,16 +1,13 @@ - diff --git a/src/app/pages/setup/4.vue b/src/app/pages/setup/4.vue index 44415ea1..806f3119 100644 --- a/src/app/pages/setup/4.vue +++ b/src/app/pages/setup/4.vue @@ -1,83 +1,19 @@ diff --git a/src/app/stores/setup.ts b/src/app/stores/setup.ts index 24307e8b..6ecde89f 100644 --- a/src/app/stores/setup.ts +++ b/src/app/stores/setup.ts @@ -4,8 +4,8 @@ export const useSetupStore = defineStore('Setup', () => { /** * @throws if unsuccessful */ - async function step4(username: string, password: string, accept: boolean) { - const response = await $fetch('/api/setup/4', { + async function step3(username: string, password: string, accept: boolean) { + const response = await $fetch('/api/setup/3', { method: 'post', body: { username, password, accept }, }); @@ -41,7 +41,7 @@ export const useSetupStore = defineStore('Setup', () => { } return { - step4, + step3, step5, runMigration, step, diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 10a12b0a..2ba7387c 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -145,6 +145,7 @@ "persistentKeepalive": "Persistent Keepalive", "address": "IP Address", "dns": "DNS", - "allowedIps": "Allowed IPs" + "allowedIps": "Allowed IPs", + "file": "File" } } diff --git a/src/server/api/setup/4.post.ts b/src/server/api/setup/3.post.ts similarity index 89% rename from src/server/api/setup/4.post.ts rename to src/server/api/setup/3.post.ts index 0869b07f..393a4deb 100644 --- a/src/server/api/setup/4.post.ts +++ b/src/server/api/setup/3.post.ts @@ -9,6 +9,6 @@ export default defineSetupEventHandler(async ({ event }) => { // TODO: validate setup step await Database.users.create(username, password); - await Database.general.setSetupStep(5); + await Database.general.setSetupStep(4); return { success: true }; }); diff --git a/src/server/api/setup/migrate.post.ts b/src/server/api/setup/migrate.post.ts index d5256f9c..0e7b8642 100644 --- a/src/server/api/setup/migrate.post.ts +++ b/src/server/api/setup/migrate.post.ts @@ -1,21 +1,25 @@ -/*import { parseCidr } from 'cidr-tools'; +import { parseCidr } from 'cidr-tools'; import { stringifyIp } from 'ip-bigint'; -import { z } from 'zod';*/ +import { z } from 'zod'; -export default defineSetupEventHandler(async (/*{ event }*/) => { - // TODO: Implement - /* +export default defineSetupEventHandler(async ({ event }) => { + const { file } = await readValidatedBody( + event, + validateZod(FileSchema, event) + ); - const { file } = await readValidatedBody(event, validateZod(fileType, event)); const schema = z.object({ server: z.object({ privateKey: z.string(), publicKey: z.string(), + // only used for cidr address: z.string(), }), clients: z.record( z.string(), z.object({ + // not used + id: z.string(), name: z.string(), address: z.string(), privateKey: z.string(), @@ -31,49 +35,37 @@ export default defineSetupEventHandler(async (/*{ event }*/) => { if (!res.success) { throw new Error('Invalid Config'); } - const system = await Database.system.get(); + const oldConfig = res.data; - const oldCidr = parseCidr(oldConfig.server.address + '/24'); - const db = { - system: { - ...system, - // TODO: migrate to db calls - interface: { - ...system.interface, - address4: oldConfig.server.address, - privateKey: oldConfig.server.privateKey, - publicKey: oldConfig.server.publicKey, - }, - userConfig: { - ...system.userConfig, - defaultDns: [...system.userConfig.defaultDns], - allowedIps: [...system.userConfig.allowedIps], - address4Range: - stringifyIp({ number: oldCidr.start, version: 4 }) + '/24', - }, - } satisfies Partial, - clients: {} as Database['clients'], - }; - for (const oldClient of Object.values(oldConfig.clients)) { - const address6 = nextIPv6(db.system, db.clients); + await Database.interfaces.updateKeyPair( + oldConfig.server.privateKey, + oldConfig.server.publicKey + ); + + const ipv4Cidr = parseCidr(oldConfig.server.address + '/24'); + const ipv6Cidr = parseCidr('fdcc:ad94:bacf:61a4::cafe:0/112'); + + await Database.interfaces.updateCidr({ + ipv4Cidr: + stringifyIp({ number: ipv4Cidr.start, version: 4 }) + + `/${ipv4Cidr.prefix}`, + ipv6Cidr: ipv6Cidr.cidr, + }); - await Database.client.create({ - address4: oldClient.address, - enabled: oldClient.enabled, - name: oldClient.name, - preSharedKey: oldClient.preSharedKey, - privateKey: oldClient.privateKey, - publicKey: oldClient.publicKey, - expiresAt: null, - oneTimeLink: null, - allowedIps: [...db.system.userConfig.allowedIps], - serverAllowedIPs: [], - persistentKeepalive: 0, - address6: address6, - mtu: 1420, + for (const clientId in oldConfig.clients) { + const clientConfig = oldConfig.clients[clientId]; + const clients = await Database.clients.getAll(); + + const ipv6Address = nextIP(6, ipv6Cidr, clients); + + await Database.clients.createFromExisting({ + ...clientConfig, + ipv4Address: clientConfig.address, + ipv6Address, }); - }*/ + } + await Database.general.setSetupStep(0); return { success: true }; }); diff --git a/src/server/database/repositories/client/service.ts b/src/server/database/repositories/client/service.ts index 10a75d2a..e00a3e72 100644 --- a/src/server/database/repositories/client/service.ts +++ b/src/server/database/repositories/client/service.ts @@ -1,7 +1,11 @@ import type { DBType } from '#db/sqlite'; import { eq, sql } from 'drizzle-orm'; import { client } from './schema'; -import type { ClientCreateType, UpdateClientType } from './types'; +import type { + ClientCreateFromExistingType, + ClientCreateType, + UpdateClientType, +} from './types'; import type { ID } from '#db/schema'; import { wgInterface, userConfig } from '#db/schema'; import { parseCidr } from 'cidr-tools'; @@ -142,4 +146,35 @@ export class ClientService { update(id: ID, data: UpdateClientType) { return this.#db.update(client).set(data).where(eq(client.id, id)).execute(); } + + async createFromExisting({ + name, + enabled, + ipv4Address, + ipv6Address, + preSharedKey, + privateKey, + publicKey, + }: ClientCreateFromExistingType) { + const clientConfig = await Database.userConfigs.get(); + + return this.#db + .insert(client) + .values({ + name, + userId: 1, + privateKey, + publicKey, + preSharedKey, + ipv4Address, + ipv6Address, + mtu: clientConfig.defaultMtu, + allowedIps: clientConfig.defaultAllowedIps, + dns: clientConfig.defaultDns, + persistentKeepalive: clientConfig.defaultPersistentKeepalive, + serverAllowedIps: [], + enabled, + }) + .execute(); + } } diff --git a/src/server/database/repositories/client/types.ts b/src/server/database/repositories/client/types.ts index 849d6747..09bbce13 100644 --- a/src/server/database/repositories/client/types.ts +++ b/src/server/database/repositories/client/types.ts @@ -5,6 +5,8 @@ import type { client } from './schema'; export type ClientType = InferSelectModel; +export type ClientNextIpType = Pick; + export type CreateClientType = Omit< ClientType, 'createdAt' | 'updatedAt' | 'id' @@ -68,3 +70,14 @@ const clientId = z.number({ message: t('zod.client.id'), coerce: true }); export const ClientGetSchema = z.object({ clientId: clientId, }); + +export type ClientCreateFromExistingType = Pick< + ClientType, + | 'name' + | 'ipv4Address' + | 'ipv6Address' + | 'privateKey' + | 'preSharedKey' + | 'publicKey' + | 'enabled' +>; diff --git a/src/server/database/repositories/oneTimeLink/types.ts b/src/server/database/repositories/oneTimeLink/types.ts index 423a61d5..c1020d46 100644 --- a/src/server/database/repositories/oneTimeLink/types.ts +++ b/src/server/database/repositories/oneTimeLink/types.ts @@ -5,8 +5,8 @@ import { z } from 'zod'; export type OneTimeLinkType = InferSelectModel; const oneTimeLinkType = z - .string({ message: t('zod.otl.otl') }) - .min(1, t('zod.otl.otl')) + .string({ message: t('zod.otl') }) + .min(1, t('zod.otl')) .pipe(safeStringRefine); export const OneTimeLinkGetSchema = z.object( diff --git a/src/server/utils/ip.ts b/src/server/utils/ip.ts index 0a5bb3dc..c5661337 100644 --- a/src/server/utils/ip.ts +++ b/src/server/utils/ip.ts @@ -1,14 +1,14 @@ import type { parseCidr } from 'cidr-tools'; import { stringifyIp } from 'ip-bigint'; -import type { ClientType } from '#db/repositories/client/types'; +import type { ClientNextIpType } from '#db/repositories/client/types'; type ParsedCidr = ReturnType; export function nextIP( version: 4 | 6, cidr: ParsedCidr, - clients: ClientType[] + clients: ClientNextIpType[] ) { let address; for (let i = cidr.start + 2n; i <= cidr.end - 1n; i++) { diff --git a/src/server/utils/types.ts b/src/server/utils/types.ts index a8dd0a7c..6fae6536 100644 --- a/src/server/utils/types.ts +++ b/src/server/utils/types.ts @@ -9,6 +9,7 @@ import type { H3Event, EventHandlerRequest } from 'h3'; */ export const t = (v: string) => v; +// TODO: use everywhere or remove export const objectMessage = t('zod.body'); export const safeStringRefine = z @@ -50,6 +51,13 @@ export const AllowedIpsSchema = z .array(AddressSchema, { message: t('zod.allowedIps') }) .min(1, { message: t('zod.allowedIps') }); +export const FileSchema = z.object( + { + file: z.string({ message: t('zod.file') }), + }, + { message: objectMessage } +); + export const schemaForType = () => // eslint-disable-next-line @typescript-eslint/no-explicit-any