From bc93e95ca0b92e483d488d143880cea175184f97 Mon Sep 17 00:00:00 2001 From: Bernd Storath <999999bst@gmail.com> Date: Fri, 17 Jan 2025 14:58:30 +0100 Subject: [PATCH] migrate to sqlite --- src/app/components/ClientCard/Address.vue | 2 +- .../components/ClientCard/OneTimeLinkBtn.vue | 6 +- src/app/components/ui/UserMenu.vue | 2 +- src/app/utils/api.ts | 6 - src/i18n/locales/en.json | 78 ++++--- src/server/api/admin/hooks.get.ts | 7 +- src/server/api/admin/hooks.post.ts | 2 +- .../client/[clientId]/configuration.get.ts | 10 +- .../api/client/[clientId]/disable.post.ts | 7 +- .../api/client/[clientId]/enable.post.ts | 7 +- .../[clientId]/generateOneTimeLink.post.ts | 6 +- .../api/client/[clientId]/index.delete.ts | 7 +- src/server/api/client/[clientId]/index.get.ts | 13 +- .../api/client/[clientId]/index.post.ts | 15 +- .../api/client/[clientId]/qrcode.svg.get.ts | 4 +- src/server/api/session.post.ts | 4 +- src/server/api/setup/4.post.ts | 4 +- src/server/api/setup/5.post.ts | 4 +- .../database/repositories/client/service.ts | 18 +- .../database/repositories/client/types.ts | 46 ++-- .../database/repositories/general/types.ts | 4 + .../database/repositories/metrics/service.ts | 0 .../database/repositories/metrics/types.ts | 4 + .../repositories/oneTimeLink/schema.ts | 2 +- .../repositories/oneTimeLink/service.ts | 17 ++ .../repositories/oneTimeLink/types.ts | 4 + .../database/repositories/user/types.ts | 38 ++++ .../database/repositories/userConfig/types.ts | 15 ++ src/server/middleware/auth.ts | 9 +- src/server/utils/types.ts | 212 +----------------- src/{server => shared}/utils/permissions.ts | 0 31 files changed, 248 insertions(+), 305 deletions(-) create mode 100644 src/server/database/repositories/general/types.ts create mode 100644 src/server/database/repositories/metrics/service.ts create mode 100644 src/server/database/repositories/metrics/types.ts create mode 100644 src/server/database/repositories/oneTimeLink/types.ts rename src/{server => shared}/utils/permissions.ts (100%) diff --git a/src/app/components/ClientCard/Address.vue b/src/app/components/ClientCard/Address.vue index e7957d30..c405ef83 100644 --- a/src/app/components/ClientCard/Address.vue +++ b/src/app/components/ClientCard/Address.vue @@ -1,6 +1,6 @@ diff --git a/src/app/components/ClientCard/OneTimeLinkBtn.vue b/src/app/components/ClientCard/OneTimeLinkBtn.vue index 303559c6..9979a025 100644 --- a/src/app/components/ClientCard/OneTimeLinkBtn.vue +++ b/src/app/components/ClientCard/OneTimeLinkBtn.vue @@ -27,8 +27,10 @@ defineProps<{ client: LocalClient }>(); const clientsStore = useClientsStore(); function showOneTimeLink(client: LocalClient) { - api - .showOneTimeLink({ clientId: client.id }) + // TODO: improve + $fetch(`/api/client/${client.id}/generateOneTimeLink`, { + method: 'post', + }) .catch((err) => alert(err.message || err.toString())) .finally(() => clientsStore.refresh().catch(console.error)); } diff --git a/src/app/components/ui/UserMenu.vue b/src/app/components/ui/UserMenu.vue index 8e956567..31547319 100644 --- a/src/app/components/ui/UserMenu.vue +++ b/src/app/components/ui/UserMenu.vue @@ -37,7 +37,7 @@ Account - + { - const system = await Database.system.get(); - return system.hooks; + const hooks = await Database.hooks.get('wg0'); + if (!hooks) { + throw new Error('Hooks not found'); + } + return hooks; }); diff --git a/src/server/api/admin/hooks.post.ts b/src/server/api/admin/hooks.post.ts index 7a0702fd..820c60f4 100644 --- a/src/server/api/admin/hooks.post.ts +++ b/src/server/api/admin/hooks.post.ts @@ -3,7 +3,7 @@ export default defineEventHandler(async (event) => { event, validateZod(hooksUpdateType, event) ); - await Database.system.updateHooks(data); + await Database.hooks.update(data); await WireGuard.saveConfig(); return { success: true }; }); diff --git a/src/server/api/client/[clientId]/configuration.get.ts b/src/server/api/client/[clientId]/configuration.get.ts index 97349f5a..e20645b5 100644 --- a/src/server/api/client/[clientId]/configuration.get.ts +++ b/src/server/api/client/[clientId]/configuration.get.ts @@ -1,11 +1,19 @@ +import { ClientGetSchema } from '#db/repositories/client/types'; + export default definePermissionEventHandler( actions.CLIENT, async ({ event }) => { const { clientId } = await getValidatedRouterParams( event, - validateZod(clientIdType) + validateZod(ClientGetSchema) ); const client = await Database.clients.get(clientId); + if (!client) { + throw createError({ + statusCode: 404, + statusMessage: 'Client not found', + }); + } const config = await WireGuard.getClientConfiguration({ clientId }); const configName = client.name .replace(/[^a-zA-Z0-9_=+.-]/g, '-') diff --git a/src/server/api/client/[clientId]/disable.post.ts b/src/server/api/client/[clientId]/disable.post.ts index aa6a506f..d4746e2d 100644 --- a/src/server/api/client/[clientId]/disable.post.ts +++ b/src/server/api/client/[clientId]/disable.post.ts @@ -1,11 +1,14 @@ +import { ClientGetSchema } from '#db/repositories/client/types'; + export default definePermissionEventHandler( actions.CLIENT, async ({ event }) => { const { clientId } = await getValidatedRouterParams( event, - validateZod(clientIdType) + validateZod(ClientGetSchema) ); - await WireGuard.disableClient({ clientId }); + await Database.clients.toggle(clientId, false); + await WireGuard.saveConfig(); return { success: true }; } ); diff --git a/src/server/api/client/[clientId]/enable.post.ts b/src/server/api/client/[clientId]/enable.post.ts index e1e8788c..d4746e2d 100644 --- a/src/server/api/client/[clientId]/enable.post.ts +++ b/src/server/api/client/[clientId]/enable.post.ts @@ -1,11 +1,14 @@ +import { ClientGetSchema } from '#db/repositories/client/types'; + export default definePermissionEventHandler( actions.CLIENT, async ({ event }) => { const { clientId } = await getValidatedRouterParams( event, - validateZod(clientIdType) + validateZod(ClientGetSchema) ); - await WireGuard.enableClient({ clientId }); + await Database.clients.toggle(clientId, false); + await WireGuard.saveConfig(); return { success: true }; } ); diff --git a/src/server/api/client/[clientId]/generateOneTimeLink.post.ts b/src/server/api/client/[clientId]/generateOneTimeLink.post.ts index 0b1f82f1..dc4287ad 100644 --- a/src/server/api/client/[clientId]/generateOneTimeLink.post.ts +++ b/src/server/api/client/[clientId]/generateOneTimeLink.post.ts @@ -1,11 +1,13 @@ +import { ClientGetSchema } from '#db/repositories/client/types'; + export default definePermissionEventHandler( actions.CLIENT, async ({ event }) => { const { clientId } = await getValidatedRouterParams( event, - validateZod(clientIdType) + validateZod(ClientGetSchema) ); - await WireGuard.generateOneTimeLink({ clientId }); + await Database.oneTimeLinks.generate(clientId); return { success: true }; } ); diff --git a/src/server/api/client/[clientId]/index.delete.ts b/src/server/api/client/[clientId]/index.delete.ts index edd100e7..686a39ac 100644 --- a/src/server/api/client/[clientId]/index.delete.ts +++ b/src/server/api/client/[clientId]/index.delete.ts @@ -1,11 +1,14 @@ +import { ClientGetSchema } from '#db/repositories/client/types'; + export default definePermissionEventHandler( actions.CLIENT, async ({ event }) => { const { clientId } = await getValidatedRouterParams( event, - validateZod(clientIdType) + validateZod(ClientGetSchema) ); - await WireGuard.deleteClient({ clientId }); + await Database.clients.delete(clientId); + await WireGuard.saveConfig(); return { success: true }; } ); diff --git a/src/server/api/client/[clientId]/index.get.ts b/src/server/api/client/[clientId]/index.get.ts index 6d5b1b49..0a4f78db 100644 --- a/src/server/api/client/[clientId]/index.get.ts +++ b/src/server/api/client/[clientId]/index.get.ts @@ -1,10 +1,19 @@ +import { ClientGetSchema } from '~~/server/database/repositories/client/types'; + export default definePermissionEventHandler( actions.CLIENT, async ({ event }) => { const { clientId } = await getValidatedRouterParams( event, - validateZod(clientIdType) + validateZod(ClientGetSchema) ); - return WireGuard.getClient({ clientId }); + const result = await Database.clients.get(clientId); + if (!result) { + throw createError({ + statusCode: 404, + statusMessage: 'Client not found', + }); + } + return result; } ); diff --git a/src/server/api/client/[clientId]/index.post.ts b/src/server/api/client/[clientId]/index.post.ts index bb18bd1a..78ff38e3 100644 --- a/src/server/api/client/[clientId]/index.post.ts +++ b/src/server/api/client/[clientId]/index.post.ts @@ -1,20 +1,23 @@ +import { + ClientGetSchema, + ClientUpdateSchema, +} from '#db/repositories/client/types'; + export default definePermissionEventHandler( actions.CLIENT, async ({ event }) => { const { clientId } = await getValidatedRouterParams( event, - validateZod(clientIdType) + validateZod(ClientGetSchema) ); const data = await readValidatedBody( event, - validateZod(clientUpdateType, event) + validateZod(ClientUpdateSchema, event) ); - await WireGuard.updateClient({ - clientId, - client: data, - }); + await Database.clients.update(clientId, data); + await WireGuard.saveConfig(); return { success: true }; } diff --git a/src/server/api/client/[clientId]/qrcode.svg.get.ts b/src/server/api/client/[clientId]/qrcode.svg.get.ts index 494b36a0..381054cd 100644 --- a/src/server/api/client/[clientId]/qrcode.svg.get.ts +++ b/src/server/api/client/[clientId]/qrcode.svg.get.ts @@ -1,9 +1,11 @@ +import { ClientGetSchema } from '#db/repositories/client/types'; + export default definePermissionEventHandler( actions.CLIENT, async ({ event }) => { const { clientId } = await getValidatedRouterParams( event, - validateZod(clientIdType) + validateZod(ClientGetSchema) ); const svg = await WireGuard.getClientQRCodeSVG({ clientId }); setHeader(event, 'Content-Type', 'image/svg+xml'); diff --git a/src/server/api/session.post.ts b/src/server/api/session.post.ts index 7b5df3e9..4cfbf35d 100644 --- a/src/server/api/session.post.ts +++ b/src/server/api/session.post.ts @@ -1,7 +1,9 @@ +import { UserLoginSchema } from '#db/repositories/user/types'; + export default defineEventHandler(async (event) => { const { username, password, remember } = await readValidatedBody( event, - validateZod(credentialsType, event) + validateZod(UserLoginSchema, event) ); const user = await Database.users.getByUsername(username); diff --git a/src/server/api/setup/4.post.ts b/src/server/api/setup/4.post.ts index de8a9c33..fd7f29c0 100644 --- a/src/server/api/setup/4.post.ts +++ b/src/server/api/setup/4.post.ts @@ -1,3 +1,5 @@ +import { UserSetupType } from '#db/repositories/user/types'; + export default defineEventHandler(async (event) => { const { done } = await Database.general.getSetupStep(); if (done) { @@ -9,7 +11,7 @@ export default defineEventHandler(async (event) => { const { username, password } = await readValidatedBody( event, - validateZod(passwordSetupType, event) + validateZod(UserSetupType, event) ); await Database.users.create(username, password); diff --git a/src/server/api/setup/5.post.ts b/src/server/api/setup/5.post.ts index a9b8f5e2..514c4166 100644 --- a/src/server/api/setup/5.post.ts +++ b/src/server/api/setup/5.post.ts @@ -1,3 +1,5 @@ +import { UserConfigSetupType } from '#db/repositories/userConfig/types'; + export default defineEventHandler(async (event) => { const { done } = await Database.general.getSetupStep(); if (done) { @@ -9,7 +11,7 @@ export default defineEventHandler(async (event) => { const { host, port } = await readValidatedBody( event, - validateZod(hostPortType, event) + validateZod(UserConfigSetupType, event) ); await Database.userConfigs.updateHostPort('wg0', host, port); await Database.general.setSetupStep(0); diff --git a/src/server/database/repositories/client/service.ts b/src/server/database/repositories/client/service.ts index f4d31776..7c8971e1 100644 --- a/src/server/database/repositories/client/service.ts +++ b/src/server/database/repositories/client/service.ts @@ -1,7 +1,7 @@ import type { DBType } from '#db/sqlite'; import { eq, sql } from 'drizzle-orm'; import { client } from './schema'; -import type { ClientCreateType } from './types'; +import type { ClientCreateType, UpdateClientType } from './types'; import type { ID } from '../../schema'; import { wgInterface, userConfig } from '../../schema'; import { parseCidr } from 'cidr-tools'; @@ -23,6 +23,10 @@ function createPreparedStatement(db: DBType) { .set({ enabled: sql.placeholder('enabled') as never as boolean }) .where(eq(client.id, sql.placeholder('id'))) .prepare(), + delete: db + .delete(client) + .where(eq(client.id, sql.placeholder('id'))) + .prepare(), }; } @@ -44,7 +48,7 @@ export class ClientService { })); } - async get(id: ID) { + get(id: ID) { return this.#statements.findById.execute({ id }); } @@ -110,7 +114,15 @@ export class ClientService { }); } - async toggle(id: ID, enabled: boolean) { + toggle(id: ID, enabled: boolean) { return this.#statements.toggle.execute({ id, enabled }); } + + delete(id: ID) { + return this.#statements.delete.execute({ id }); + } + + update(id: ID, data: UpdateClientType) { + return this.#db.update(client).set(data).where(eq(client.id, id)).prepare(); + } } diff --git a/src/server/database/repositories/client/types.ts b/src/server/database/repositories/client/types.ts index a6731cec..45a5bd69 100644 --- a/src/server/database/repositories/client/types.ts +++ b/src/server/database/repositories/client/types.ts @@ -25,52 +25,54 @@ export type UpdateClientType = Omit< >; const name = zod - .string({ message: 'zod.name' }) - .min(1, 'zod.nameMin') + .string({ message: 'zod.client.name' }) + .min(1, 'zod.client.nameMin') .pipe(safeStringRefine); const expiresAt = zod - .string({ message: 'zod.expireDate' }) - .min(1, 'zod.expireDateMin') + .string({ message: 'zod.client.expireDate' }) + .min(1, 'zod.client.expireDateMin') .pipe(safeStringRefine) .nullable(); const address = zod - .string({ message: 'zod.address' }) - .min(1, { message: 'zod.addressMin' }) + .string({ message: 'zod.client.address' }) + .min(1, { message: 'zod.client.addressMin' }) .pipe(safeStringRefine); const address4 = zod - .string({ message: 'zod.address4' }) - .min(1, { message: 'zod.address4Min' }) + .string({ message: 'zod.client.address4' }) + .min(1, { message: 'zod.client.address4Min' }) .pipe(safeStringRefine); const address6 = zod - .string({ message: 'zod.address6' }) - .min(1, { message: 'zod.address6Min' }) + .string({ message: 'zod.client.address6' }) + .min(1, { message: 'zod.client.address6Min' }) .pipe(safeStringRefine); const allowedIps = zod - .array(address, { message: 'zod.allowedIps' }) - .min(1, { message: 'zod.allowedIpsMin' }); + .array(address, { message: 'zod.client.allowedIps' }) + .min(1, { message: 'zod.client.allowedIpsMin' }); const serverAllowedIps = zod.array(address, { message: 'zod.serverAllowedIps', }); const mtu = zod - .number({ message: 'zod.mtu' }) - .min(1280, { message: 'zod.mtuMin' }) - .max(9000, { message: 'zod.mtuMax' }); + .number({ message: 'zod.client.mtu' }) + .min(1280, { message: 'zod.client.mtuMin' }) + .max(9000, { message: 'zod.client.mtuMax' }); const persistentKeepalive = zod - .number({ message: 'zod.persistentKeepalive' }) - .min(0, 'zod.persistentKeepaliveMin') - .max(65535, 'zod.persistentKeepaliveMax'); + .number({ message: 'zod.client.persistentKeepalive' }) + .min(0, 'zod.client.persistentKeepaliveMin') + .max(65535, 'zod.client.persistentKeepaliveMax'); const enabled = zod.boolean({ message: 'zod.enabled' }); -const dns = zod.array(address, { message: 'zod.dns' }).min(1, 'zod.dnsMin'); +const dns = zod + .array(address, { message: 'zod.client.dns' }) + .min(1, 'zod.client.dnsMin'); export const ClientCreateSchema = zod.object({ name: name, @@ -93,3 +95,9 @@ export const ClientUpdateSchema = schemaForType()( dns: dns, }) ); + +const clientId = zod.number({ message: 'zod.client.id' }); + +export const ClientGetSchema = zod.object({ + clientId: clientId, +}); diff --git a/src/server/database/repositories/general/types.ts b/src/server/database/repositories/general/types.ts new file mode 100644 index 00000000..902859cb --- /dev/null +++ b/src/server/database/repositories/general/types.ts @@ -0,0 +1,4 @@ +import type { InferSelectModel } from 'drizzle-orm'; +import type { general } from './schema'; + +export type GeneralType = InferSelectModel; diff --git a/src/server/database/repositories/metrics/service.ts b/src/server/database/repositories/metrics/service.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/server/database/repositories/metrics/types.ts b/src/server/database/repositories/metrics/types.ts new file mode 100644 index 00000000..45c4bcad --- /dev/null +++ b/src/server/database/repositories/metrics/types.ts @@ -0,0 +1,4 @@ +import type { InferSelectModel } from 'drizzle-orm'; +import type { prometheus } from './schema'; + +export type PrometheusType = InferSelectModel; diff --git a/src/server/database/repositories/oneTimeLink/schema.ts b/src/server/database/repositories/oneTimeLink/schema.ts index 3903e778..dd69c2fd 100644 --- a/src/server/database/repositories/oneTimeLink/schema.ts +++ b/src/server/database/repositories/oneTimeLink/schema.ts @@ -5,7 +5,7 @@ import { client } from '../../schema'; export const oneTimeLink = sqliteTable('one_time_links_table', { id: int().primaryKey({ autoIncrement: true }), - oneTimeLink: text('one_time_link').notNull(), + oneTimeLink: text('one_time_link').notNull().unique(), expiresAt: text('expires_at').notNull(), clientId: int() .notNull() diff --git a/src/server/database/repositories/oneTimeLink/service.ts b/src/server/database/repositories/oneTimeLink/service.ts index 8d1ec5b3..48b8d8d1 100644 --- a/src/server/database/repositories/oneTimeLink/service.ts +++ b/src/server/database/repositories/oneTimeLink/service.ts @@ -2,6 +2,7 @@ import type { DBType } from '#db/sqlite'; import { eq, sql } from 'drizzle-orm'; import { oneTimeLink } from './schema'; import type { ID } from '../../schema'; +import CRC32 from 'crc-32'; function createPreparedStatement(db: DBType) { return { @@ -9,6 +10,14 @@ function createPreparedStatement(db: DBType) { .delete(oneTimeLink) .where(eq(oneTimeLink.id, sql.placeholder('id'))) .prepare(), + create: db + .insert(oneTimeLink) + .values({ + clientId: sql.placeholder('id'), + oneTimeLink: sql.placeholder('oneTimeLink'), + expiresAt: sql.placeholder('expiresAt'), + }) + .prepare(), }; } @@ -22,4 +31,12 @@ export class OneTimeLinkService { delete(id: ID) { return this.#statements.delete.execute({ id }); } + + generate(id: ID) { + const key = `${id}-${Math.floor(Math.random() * 1000)}`; + const oneTimeLink = Math.abs(CRC32.str(key)).toString(16); + const expiresAt = new Date(Date.now() + 5 * 60 * 1000).toISOString(); + + return this.#statements.create.execute({ id, oneTimeLink, expiresAt }); + } } diff --git a/src/server/database/repositories/oneTimeLink/types.ts b/src/server/database/repositories/oneTimeLink/types.ts new file mode 100644 index 00000000..ea0ac729 --- /dev/null +++ b/src/server/database/repositories/oneTimeLink/types.ts @@ -0,0 +1,4 @@ +import type { InferSelectModel } from 'drizzle-orm'; +import type { oneTimeLink } from './schema'; + +export type OneTimeLinkType = InferSelectModel; diff --git a/src/server/database/repositories/user/types.ts b/src/server/database/repositories/user/types.ts index 500d309d..2a1017e4 100644 --- a/src/server/database/repositories/user/types.ts +++ b/src/server/database/repositories/user/types.ts @@ -2,3 +2,41 @@ import type { InferSelectModel } from 'drizzle-orm'; import type { user } from './schema'; export type UserType = InferSelectModel; + +const username = zod + .string({ message: 'zod.user.username' }) + .min(8, 'zod.user.usernameMin') + .pipe(safeStringRefine); + +const password = zod + .string({ message: 'zod.user.password' }) + .min(12, 'zod.user.passwordMin') + .regex(/[A-Z]/, 'zod.user.passwordUppercase') + .regex(/[a-z]/, 'zod.user.passwordLowercase') + .regex(/\d/, 'zod.user.passwordNumber') + .regex(/[!@#$%^&*(),.?":{}|<>]/, 'zod.user.passwordSpecial') + .pipe(safeStringRefine); + +const remember = zod.boolean({ message: 'zod.user.remember' }); + +export const UserLoginSchema = zod.object( + { + username: username, + password: password, + remember: remember, + }, + { message: objectMessage } +); + +const accept = zod.boolean().refine((val) => val === true, { + message: 'zod.user.accept', +}); + +export const UserSetupType = zod.object( + { + username: username, + password: password, + accept: accept, + }, + { message: objectMessage } +); diff --git a/src/server/database/repositories/userConfig/types.ts b/src/server/database/repositories/userConfig/types.ts index db253669..91a4721b 100644 --- a/src/server/database/repositories/userConfig/types.ts +++ b/src/server/database/repositories/userConfig/types.ts @@ -2,3 +2,18 @@ import type { InferSelectModel } from 'drizzle-orm'; import type { userConfig } from './schema'; export type UserConfigType = InferSelectModel; + +const host = zod + .string({ message: 'zod.userConfig.host' }) + .min(1, 'zod.userConfig.hostMin') + .pipe(safeStringRefine); + +const port = zod + .number({ message: 'zod.userConfig.port' }) + .min(1, 'zod.userConfig.portMin') + .max(65535, 'zod.userConfig.portMax'); + +export const UserConfigSetupType = zod.object({ + host: host, + port: port, +}); diff --git a/src/server/middleware/auth.ts b/src/server/middleware/auth.ts index 91410ea2..2f608d0f 100644 --- a/src/server/middleware/auth.ts +++ b/src/server/middleware/auth.ts @@ -1,5 +1,6 @@ export default defineEventHandler(async (event) => { - /*const url = getRequestURL(event); + // TODO: improve, wrapper or smth + const url = getRequestURL(event); const session = await useWGSession(event); // Api handled by session, Setup handled with setup middleware @@ -21,15 +22,15 @@ export default defineEventHandler(async (event) => { } if (url.pathname.startsWith('/admin')) { - const user = await Database.user.findById(session.data.userId); + const user = await Database.users.get(session.data.userId); if (!user) { return sendRedirect(event, '/login', 302); } - if (user.role !== 'ADMIN') { + if (user.role !== roles.ADMIN) { throw createError({ statusCode: 403, statusMessage: 'Not allowed to access Admin Panel', }); } - }*/ + } }); diff --git a/src/server/utils/types.ts b/src/server/utils/types.ts index 5d1b9910..b25a2e5c 100644 --- a/src/server/utils/types.ts +++ b/src/server/utils/types.ts @@ -1,10 +1,10 @@ -import type { ZodSchema, ZodTypeDef } from 'zod'; +import type { ZodSchema } from 'zod'; import z from 'zod'; import type { H3Event, EventHandlerRequest } from 'h3'; export { default as zod } from 'zod'; -const objectMessage = 'zod.body'; +export const objectMessage = 'zod.body'; export const safeStringRefine = z .string() @@ -13,210 +13,8 @@ export const safeStringRefine = z { message: 'zod.stringMalformed' } ); -const host = z - .string({ message: 'zod.host' }) - .min(1, 'zod.hostMin') - .pipe(safeStringRefine); - -const port = z - .number({ message: 'zod.port' }) - .min(1, 'zod.portMin') - .max(65535, 'zod.portMax'); - -export const hostPortType = z.object({ - host: host, - port: port, -}); - -const id = z.string().uuid('zod.id').pipe(safeStringRefine); - -export const clientIdType = z.object( - { - clientId: id, - }, - { message: objectMessage } -); - -const oneTimeLink = z - .string({ message: 'zod.otl' }) - .min(1, 'zod.otlMin') - .pipe(safeStringRefine); - -export const oneTimeLinkType = z.object( - { - oneTimeLink: oneTimeLink, - }, - { message: objectMessage } -); - -const name = z - .string({ message: 'zod.name' }) - .min(1, 'zod.nameMin') - .pipe(safeStringRefine); - -const expireDate = z - .string({ message: 'zod.expireDate' }) - .min(1, 'zod.expireDateMin') - .pipe(safeStringRefine) - .nullable(); - -export const createType = z.object( - { - name: name, - expireDate: expireDate, - }, - { message: objectMessage } -); - -const file = z.string({ message: 'zod.file' }).pipe(safeStringRefine); -const file_ = z.instanceof(File, { message: 'zod.file' }); - -export const fileType = z.object( - { - file: file, - }, - { message: objectMessage } -); -export const fileType_ = z.object( - { - file: file_, - }, - { message: objectMessage } -); - -const username = z - .string({ message: 'zod.username' }) - .min(8, 'zod.usernameMin') - .pipe(safeStringRefine); - -const password = z - .string({ message: 'zod.password' }) - .min(12, 'zod.passwordMin') - .regex(/[A-Z]/, 'zod.passwordUppercase') - .regex(/[a-z]/, 'zod.passwordLowercase') - .regex(/\d/, 'zod.passwordNumber') - .regex(/[!@#$%^&*(),.?":{}|<>]/, 'zod.passwordSpecial') - .pipe(safeStringRefine); - -const remember = z.boolean({ message: 'zod.remember' }); - -export const credentialsType = z.object( - { - username: username, - password: password, - remember: remember, - }, - { message: objectMessage } -); - -export const passwordType = z.object( - { - username: username, - password: password, - }, - { message: objectMessage } -); - -const accept = z.boolean().refine((val) => val === true, { - message: 'zod.accept', -}); - -export const passwordSetupType = z.object( - { - username: username, - password: password, - accept: accept, - }, - { message: objectMessage } -); - -const address = z - .string({ message: 'zod.address' }) - .min(1, { message: 'zod.addressMin' }) - .pipe(safeStringRefine); - -const address4 = z - .string({ message: 'zod.address4' }) - .min(1, { message: 'zod.address4Min' }) - .pipe(safeStringRefine); - -const address6 = z - .string({ message: 'zod.address6' }) - .min(1, { message: 'zod.address6Min' }) - .pipe(safeStringRefine); - -const allowedIps = z - .array(address, { message: 'zod.allowedIps' }) - .min(1, { message: 'zod.allowedIpsMin' }); - -const mtu = z - .number({ message: 'zod.mtu' }) - .min(1280, { message: 'zod.mtuMin' }) - .max(9000, { message: 'zod.mtuMax' }); - -const persistentKeepalive = z - .number({ message: 'zod.persistentKeepalive' }) - .min(0, 'zod.persistentKeepaliveMin') - .max(65535, 'zod.persistentKeepaliveMax'); - -export const clientUpdateType = z.object({ - name: name, - enabled: z.boolean(), - expiresAt: expireDate, - address4: address4, - address6: address6, - allowedIps: allowedIps, - serverAllowedIPs: z.array(address, { message: 'zod.serverAllowedIPs' }), - mtu: mtu, - persistentKeepalive: persistentKeepalive, -}); - -export const generalUpdateType = z.object({ - sessionTimeout: z.number({ message: 'zod.sessionTimeout' }), -}); - -const device = z - .string({ message: 'zod.device' }) - .min(1, 'zod.deviceMin') - .pipe(safeStringRefine); - -export const interfaceUpdateType = z.object({ - mtu: mtu, - port: port, - device: device, -}); - -export const userConfigUpdateType = z.object({ - host: host, - port: port, - allowedIps: allowedIps, - defaultDns: z.array(address, { message: 'zod.dns' }), - mtu: mtu, - persistentKeepalive: persistentKeepalive, -}); - -const hook = z.string({ message: 'zod.hook' }).pipe(safeStringRefine); - -export const hooksUpdateType = z.object({ - PreUp: hook, - PostUp: hook, - PreDown: hook, - PostDown: hook, -}); - -export const cidrUpdateType = z.object({ - address4: address, - address6: address, -}); - -// from https://github.com/airjp73/rvf/blob/7e7c35d98015ea5ecff5affaf89f78296e84e8b9/packages/zod-form-data/src/helpers.ts#L117 -type FormDataLikeInput = { - [Symbol.iterator](): IterableIterator<[string, FormDataEntryValue]>; - entries(): IterableIterator<[string, FormDataEntryValue]>; -}; - export function validateZod( - schema: ZodSchema | ZodSchema, + schema: ZodSchema, event?: H3Event ) { return async (data: unknown) => { @@ -247,7 +45,3 @@ export function validateZod( } }; } - -export type DeepWriteable = { - -readonly [P in keyof T]: DeepWriteable; -}; diff --git a/src/server/utils/permissions.ts b/src/shared/utils/permissions.ts similarity index 100% rename from src/server/utils/permissions.ts rename to src/shared/utils/permissions.ts