Browse Source

migrate to sqlite

pull/1619/head
Bernd Storath 3 months ago
parent
commit
bc93e95ca0
  1. 2
      src/app/components/ClientCard/Address.vue
  2. 6
      src/app/components/ClientCard/OneTimeLinkBtn.vue
  3. 2
      src/app/components/ui/UserMenu.vue
  4. 6
      src/app/utils/api.ts
  5. 78
      src/i18n/locales/en.json
  6. 7
      src/server/api/admin/hooks.get.ts
  7. 2
      src/server/api/admin/hooks.post.ts
  8. 10
      src/server/api/client/[clientId]/configuration.get.ts
  9. 7
      src/server/api/client/[clientId]/disable.post.ts
  10. 7
      src/server/api/client/[clientId]/enable.post.ts
  11. 6
      src/server/api/client/[clientId]/generateOneTimeLink.post.ts
  12. 7
      src/server/api/client/[clientId]/index.delete.ts
  13. 13
      src/server/api/client/[clientId]/index.get.ts
  14. 15
      src/server/api/client/[clientId]/index.post.ts
  15. 4
      src/server/api/client/[clientId]/qrcode.svg.get.ts
  16. 4
      src/server/api/session.post.ts
  17. 4
      src/server/api/setup/4.post.ts
  18. 4
      src/server/api/setup/5.post.ts
  19. 18
      src/server/database/repositories/client/service.ts
  20. 46
      src/server/database/repositories/client/types.ts
  21. 4
      src/server/database/repositories/general/types.ts
  22. 0
      src/server/database/repositories/metrics/service.ts
  23. 4
      src/server/database/repositories/metrics/types.ts
  24. 2
      src/server/database/repositories/oneTimeLink/schema.ts
  25. 17
      src/server/database/repositories/oneTimeLink/service.ts
  26. 4
      src/server/database/repositories/oneTimeLink/types.ts
  27. 38
      src/server/database/repositories/user/types.ts
  28. 15
      src/server/database/repositories/userConfig/types.ts
  29. 9
      src/server/middleware/auth.ts
  30. 212
      src/server/utils/types.ts
  31. 0
      src/shared/utils/permissions.ts

2
src/app/components/ClientCard/Address.vue

@ -1,6 +1,6 @@
<template>
<span class="inline-block">
{{ client.address4 }}, {{ client.address6 }}
{{ client.ipv4Address }}, {{ client.ipv6Address }}
</span>
</template>

6
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));
}

2
src/app/components/ui/UserMenu.vue

@ -37,7 +37,7 @@
Account
</NuxtLink>
</DropdownMenuItem>
<DropdownMenuItem v-if="authStore.userData?.role === 'ADMIN'">
<DropdownMenuItem v-if="authStore.userData?.role === roles.ADMIN">
<NuxtLink
to="/admin"
class="block px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white"

6
src/app/utils/api.ts

@ -18,12 +18,6 @@ class API {
});
}
async showOneTimeLink({ clientId }: { clientId: string }) {
return $fetch(`/api/client/${clientId}/generateOneTimeLink`, {
method: 'post',
});
}
async restoreConfiguration(file: string) {
return $fetch('/api/wireguard/restore', {
method: 'put',

78
src/i18n/locales/en.json

@ -41,36 +41,7 @@
"zod": {
"stringMalformed": "String is malformed",
"id": "Client ID must be a valid UUID",
"address": "IP Address must be a valid string",
"addressMin": "IP Address must be a be at least 1 Character",
"address4": "IPv4 Address must be a valid string",
"address4Min": "IPv4 Address must be a be at least 1 Character",
"address6": "IPv6 Address must be a valid string",
"address6Min": "IPv6 Address must be a be at least 1 Character",
"allowedIps": "Allowed IPs must be a valid array of strings",
"allowedIpsMin": "Allowed IPs must have at least 1 item",
"serverAllowedIps": "Allowed IPs must be a valid array of strings",
"name": "Name must be a valid string",
"nameMin": "Name must be at least 1 Character",
"mtu": "MTU must be a valid number",
"mtuMin": "MTU must be at least 1280",
"mtuMax": "MTU must be at most 9000",
"persistentKeepalive": "Persistent Keepalive must be a valid number",
"persistentKeepaliveMin": "Persistent Keepalive must be at least 0",
"persistentKeepaliveMax": "Persistent Keepalive must be at most 65535",
"file": "File must be a valid string",
"username": "Username must be a valid string",
"usernameMin": "Username must be at least 8 Characters",
"password": "Password must be a valid string",
"passwordMin": "Password must be at least 12 Characters",
"passwordUppercase": "Password must have at least 1 uppercase letter",
"passwordLowercase": "Password must have at least 1 lowercase letter",
"passwordNumber": "Password must have at least 1 number",
"passwordSpecial": "Password must have at least 1 special character",
"accept": "Please accept the condition",
"remember": "Remember must be a valid boolean",
"expireDate": "expiredDate must be a valid string",
"expireDateMin": "expiredDate must be at least 1 Character",
"otl": "oneTimeLink must be a valid string",
"otlMin": "oneTimeLink must be at least 1 Character",
"features": "key must be a valid string",
@ -81,16 +52,53 @@
"statBool": "enabled must be a valid boolean",
"statNumber": "chartType must be a valid number",
"body": "Body must be a valid object",
"host": "Host must be a valid string",
"hostMin": "Host must contain at least 1 character",
"port": "Port must be a valid number",
"portMin": "Port must be at least 1",
"portMax": "Port must be at most 65535",
"sessionTimeout": "Session Timeout must be a valid number",
"device": "Device must be a valid string",
"deviceMin": "Device must be at least 1 Character",
"hook": "Hook must be a valid string",
"dns": "DNS must be a valid array of strings"
"client": {
"id": "Client ID must be a valid number",
"name": "Name must be a valid string",
"nameMin": "Name must be at least 1 Character",
"mtu": "MTU must be a valid number",
"mtuMin": "MTU must be at least 1280",
"mtuMax": "MTU must be at most 9000",
"persistentKeepalive": "Persistent Keepalive must be a valid number",
"persistentKeepaliveMin": "Persistent Keepalive must be at least 0",
"persistentKeepaliveMax": "Persistent Keepalive must be at most 65535",
"expireDate": "expiredDate must be a valid string",
"expireDateMin": "expiredDate must be at least 1 Character",
"address": "IP Address must be a valid string",
"addressMin": "IP Address must be a be at least 1 Character",
"address4": "IPv4 Address must be a valid string",
"address4Min": "IPv4 Address must be a be at least 1 Character",
"address6": "IPv6 Address must be a valid string",
"address6Min": "IPv6 Address must be a be at least 1 Character",
"allowedIps": "Allowed IPs must be a valid array of strings",
"allowedIpsMin": "Allowed IPs must have at least 1 item",
"serverAllowedIps": "Allowed IPs must be a valid array of strings",
"dns": "DNS must be a valid array of strings",
"dnsMin": "DNS must have at least 1 item"
},
"user": {
"username": "Username must be a valid string",
"usernameMin": "Username must be at least 8 Characters",
"password": "Password must be a valid string",
"passwordMin": "Password must be at least 12 Characters",
"passwordUppercase": "Password must have at least 1 uppercase letter",
"passwordLowercase": "Password must have at least 1 lowercase letter",
"passwordNumber": "Password must have at least 1 number",
"passwordSpecial": "Password must have at least 1 special character",
"remember": "Remember must be a valid boolean",
"accept": "Please accept the condition"
},
"userConfig": {
"host": "Host must be a valid string",
"hostMin": "Host must contain at least 1 character",
"port": "Port must be a valid number",
"portMin": "Port must be at least 1",
"portMax": "Port must be at most 65535"
}
},
"name": "Name",
"username": "Username",

7
src/server/api/admin/hooks.get.ts

@ -1,4 +1,7 @@
export default defineEventHandler(async () => {
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;
});

2
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 };
});

10
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, '-')

7
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 };
}
);

7
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 };
}
);

6
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 };
}
);

7
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 };
}
);

13
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;
}
);

15
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 };
}

4
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');

4
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);

4
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);

4
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);

18
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();
}
}

46
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<UpdateClientType>()(
dns: dns,
})
);
const clientId = zod.number({ message: 'zod.client.id' });
export const ClientGetSchema = zod.object({
clientId: clientId,
});

4
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<typeof general>;

0
src/server/database/repositories/metrics/service.ts

4
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<typeof prometheus>;

2
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()

17
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 });
}
}

4
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<typeof oneTimeLink>;

38
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<typeof user>;
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 }
);

15
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<typeof userConfig>;
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,
});

9
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',
});
}
}*/
}
});

212
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<T>(
schema: ZodSchema<T> | ZodSchema<T, ZodTypeDef, FormData | FormDataLikeInput>,
schema: ZodSchema<T>,
event?: H3Event<EventHandlerRequest>
) {
return async (data: unknown) => {
@ -247,7 +45,3 @@ export function validateZod<T>(
}
};
}
export type DeepWriteable<T> = {
-readonly [P in keyof T]: DeepWriteable<T[P]>;
};

0
src/server/utils/permissions.ts → src/shared/utils/permissions.ts

Loading…
Cancel
Save