Browse Source

start improving zod translations

pull/1661/head
Bernd Storath 6 months ago
parent
commit
4bbcc1c10a
  1. 26
      src/i18n/locales/en.json
  2. 2
      src/server/api/client/[clientId]/configuration.get.ts
  3. 2
      src/server/api/client/[clientId]/disable.post.ts
  4. 2
      src/server/api/client/[clientId]/enable.post.ts
  5. 2
      src/server/api/client/[clientId]/generateOneTimeLink.post.ts
  6. 2
      src/server/api/client/[clientId]/index.delete.ts
  7. 2
      src/server/api/client/[clientId]/index.post.ts
  8. 2
      src/server/api/client/[clientId]/qrcode.svg.get.ts
  9. 2
      src/server/api/client/index.post.ts
  10. 7
      src/server/api/me/index.post.ts
  11. 7
      src/server/api/me/password.post.ts
  12. 2
      src/server/api/setup/4.post.ts
  13. 1
      src/server/api/setup/5.post.ts
  14. 8
      src/server/database/repositories/client/types.ts
  15. 54
      src/server/database/repositories/user/types.ts
  16. 2
      src/server/routes/cnf/[oneTimeLink].ts
  17. 58
      src/server/utils/types.ts

26
src/i18n/locales/en.json

@ -39,6 +39,15 @@
"migration": "Restore the backup"
},
"zod": {
"generic": {
"required": "{0} is required",
"validNumber": "{0} must be a valid number",
"validString": "{0} must be a valid string",
"validBoolean": "{0} must be a valid boolean",
"validStringArray": "{0} must be a valid array of strings",
"stringMin": "{0} must be at least {1} Character",
"numberMin": "{0} must be at least {1}"
},
"client": {
"id": "Client ID must be a valid number",
"name": "Name must be a valid string",
@ -52,20 +61,17 @@
"serverAllowedIps": "Allowed IPs must be a valid array of strings"
},
"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",
"username": "Username",
"password": "Password",
"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",
"name": "Name must be a valid string",
"nameMin": "Name must be at least 1 Character",
"email": "Email must be a valid string",
"emailMin": "Email must be at least 1 Character",
"remember": "Remember",
"accept": "Accept",
"acceptTrue": "Accept Conditions to continue",
"name": "Name",
"email": "Email",
"emailInvalid": "Email must be a valid email",
"passwordMatch": "Passwords must match"
},

2
src/server/api/client/[clientId]/configuration.get.ts

@ -6,7 +6,7 @@ export default definePermissionEventHandler(
async ({ event, checkPermissions }) => {
const { clientId } = await getValidatedRouterParams(
event,
validateZod(ClientGetSchema)
validateZod(ClientGetSchema, event)
);
const client = await Database.clients.get(clientId);
checkPermissions(client);

2
src/server/api/client/[clientId]/disable.post.ts

@ -6,7 +6,7 @@ export default definePermissionEventHandler(
async ({ event, checkPermissions }) => {
const { clientId } = await getValidatedRouterParams(
event,
validateZod(ClientGetSchema)
validateZod(ClientGetSchema, event)
);
const client = await Database.clients.get(clientId);

2
src/server/api/client/[clientId]/enable.post.ts

@ -6,7 +6,7 @@ export default definePermissionEventHandler(
async ({ event, checkPermissions }) => {
const { clientId } = await getValidatedRouterParams(
event,
validateZod(ClientGetSchema)
validateZod(ClientGetSchema, event)
);
const client = await Database.clients.get(clientId);

2
src/server/api/client/[clientId]/generateOneTimeLink.post.ts

@ -6,7 +6,7 @@ export default definePermissionEventHandler(
async ({ event, checkPermissions }) => {
const { clientId } = await getValidatedRouterParams(
event,
validateZod(ClientGetSchema)
validateZod(ClientGetSchema, event)
);
const client = await Database.clients.get(clientId);

2
src/server/api/client/[clientId]/index.delete.ts

@ -6,7 +6,7 @@ export default definePermissionEventHandler(
async ({ event, checkPermissions }) => {
const { clientId } = await getValidatedRouterParams(
event,
validateZod(ClientGetSchema)
validateZod(ClientGetSchema, event)
);
const client = await Database.clients.get(clientId);

2
src/server/api/client/[clientId]/index.post.ts

@ -9,7 +9,7 @@ export default definePermissionEventHandler(
async ({ event, checkPermissions }) => {
const { clientId } = await getValidatedRouterParams(
event,
validateZod(ClientGetSchema)
validateZod(ClientGetSchema, event)
);
const data = await readValidatedBody(

2
src/server/api/client/[clientId]/qrcode.svg.get.ts

@ -6,7 +6,7 @@ export default definePermissionEventHandler(
async ({ event, checkPermissions }) => {
const { clientId } = await getValidatedRouterParams(
event,
validateZod(ClientGetSchema)
validateZod(ClientGetSchema, event)
);
const client = await Database.clients.get(clientId);

2
src/server/api/client/index.post.ts

@ -6,7 +6,7 @@ export default definePermissionEventHandler(
async ({ event }) => {
const { name, expiresAt } = await readValidatedBody(
event,
validateZod(ClientCreateSchema)
validateZod(ClientCreateSchema, event)
);
await Database.clients.create({ name, expiresAt });

7
src/server/api/me/index.post.ts

@ -3,11 +3,14 @@ import { UserUpdateSchema } from '#db/repositories/user/types';
export default definePermissionEventHandler(
'me',
'update',
async ({ event, user }) => {
async ({ event, user, checkPermissions }) => {
const { name, email } = await readValidatedBody(
event,
validateZod(UserUpdateSchema)
validateZod(UserUpdateSchema, event)
);
checkPermissions(user);
await Database.users.update(user.id, name, email);
return { success: true };
}

7
src/server/api/me/password.post.ts

@ -3,11 +3,14 @@ import { UserUpdatePasswordSchema } from '#db/repositories/user/types';
export default definePermissionEventHandler(
'me',
'update',
async ({ event, user }) => {
async ({ event, user, checkPermissions }) => {
const { newPassword, currentPassword } = await readValidatedBody(
event,
validateZod(UserUpdatePasswordSchema)
validateZod(UserUpdatePasswordSchema, event)
);
checkPermissions(user);
await Database.users.updatePassword(user.id, currentPassword, newPassword);
return { success: true };
}

2
src/server/api/setup/4.post.ts

@ -6,6 +6,8 @@ export default defineSetupEventHandler(async ({ event }) => {
validateZod(UserSetupSchema, event)
);
// TODO: validate setup step
await Database.users.create(username, password);
await Database.general.setSetupStep(5);
return { success: true };

1
src/server/api/setup/5.post.ts

@ -5,6 +5,7 @@ export default defineSetupEventHandler(async ({ event }) => {
event,
validateZod(UserConfigSetupSchema, event)
);
// TODO: validate setup step
await Database.userConfigs.updateHostPort(host, port);
await Database.general.setSetupStep(0);
return { success: true };

8
src/server/database/repositories/client/types.ts

@ -18,13 +18,13 @@ export type UpdateClientType = Omit<
>;
const name = z
.string({ message: 'zod.client.name' })
.min(1, 'zod.client.nameMin')
.string({ message: '!zod.generic.validString:zod.client.name' })
.min(1, '!zod.generic.stringMinOne:zod.client.nameMin')
.pipe(safeStringRefine);
const expiresAt = z
.string({ message: 'zod.client.expireDate' })
.min(1, 'zod.client.expireDateMin')
.string({ message: '!zod.generic.validString:zod.client.expireDate' })
.min(1, '!zod.generic.stringMinOne:zod.client.expireDateMin')
.pipe(safeStringRefine)
.nullable();

54
src/server/database/repositories/user/types.ts

@ -4,33 +4,23 @@ import z from 'zod';
export type UserType = InferSelectModel<typeof user>;
const t = (v: string) => v;
const username = z
.string({ message: 'zod.user.username' })
.min(8, 'zod.user.usernameMin')
.string({ message: t('zod.user.username') })
.min(8, t('zod.user.username'))
.pipe(safeStringRefine);
const password = z
.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 = z.boolean({ message: 'zod.user.remember' });
const name = z
.string({ message: 'zod.user.name' })
.min(1, 'zod.user.nameMin')
.string({ message: t('zod.user.password') })
.min(12, t('zod.user.password'))
.regex(/[A-Z]/, t('zod.user.passwordUppercase'))
.regex(/[a-z]/, t('zod.user.passwordLowercase'))
.regex(/\d/, t('zod.user.passwordNumber'))
.regex(/[!@#$%^&*(),.?":{}|<>]/, t('zod.user.passwordSpecial'))
.pipe(safeStringRefine);
const email = z
.string({ message: 'zod.user.email' })
.min(5, 'zod.user.emailMin')
.email({ message: 'zod.user.emailInvalid' })
.pipe(safeStringRefine)
.nullable();
const remember = z.boolean({ message: t('zod.user.remember') });
export const UserLoginSchema = z.object(
{
@ -41,9 +31,11 @@ export const UserLoginSchema = z.object(
{ message: objectMessage }
);
const accept = z.boolean().refine((val) => val === true, {
message: 'zod.user.accept',
});
const accept = z
.boolean({ message: t('zod.user.accept') })
.refine((val) => val === true, {
message: t('zod.user.acceptTrue'),
});
export const UserSetupSchema = z.object(
{
@ -54,6 +46,18 @@ export const UserSetupSchema = z.object(
{ message: objectMessage }
);
const name = z
.string({ message: t('zod.user.name') })
.min(1, 'zod.user.name')
.pipe(safeStringRefine);
const email = z
.string({ message: t('zod.user.email') })
.min(5, t('zod.user.email'))
.email({ message: t('zod.user.emailInvalid') })
.pipe(safeStringRefine)
.nullable();
export const UserUpdateSchema = z.object(
{
name: name,
@ -72,5 +76,5 @@ export const UserUpdatePasswordSchema = z
{ message: objectMessage }
)
.refine((val) => val.newPassword === val.confirmPassword, {
message: 'zod.user.passwordMatch',
message: t('zod.user.passwordMatch'),
});

2
src/server/routes/cnf/[oneTimeLink].ts

@ -3,7 +3,7 @@ import { OneTimeLinkGetSchema } from '#db/repositories/oneTimeLink/types';
export default defineEventHandler(async (event) => {
const { oneTimeLink } = await getValidatedRouterParams(
event,
validateZod(OneTimeLinkGetSchema)
validateZod(OneTimeLinkGetSchema, event)
);
const clients = await WireGuard.getAllClients();
const client = clients.find(

58
src/server/utils/types.ts

@ -52,26 +52,68 @@ export const schemaForType =
export function validateZod<T>(
schema: ZodSchema<T>,
event?: H3Event<EventHandlerRequest>
event: H3Event<EventHandlerRequest>
) {
return async (data: unknown) => {
let t: null | ((key: string) => string) = null;
if (event) {
t = await useTranslation(event);
}
try {
return await schema.parseAsync(data);
} catch (error) {
let message = 'Unexpected Error';
if (error instanceof z.ZodError) {
const t = await useTranslation(event);
message = error.issues
.map((v) => {
let m = v.message;
if (t) {
m = t(m);
let newMessage = null;
if (v.message.startsWith('zod.')) {
switch (v.code) {
case 'too_small':
switch (v.type) {
case 'string':
newMessage = t('zod.generic.stringMin', [
t(v.message),
v.minimum,
]);
break;
case 'number':
newMessage = t('zod.generic.numberMin', [
t(v.message),
v.minimum,
]);
break;
}
break;
case 'invalid_type': {
if (v.received === 'null' || v.received === 'undefined') {
newMessage = t('zod.generic.required', [
v.path.join('.'),
]);
} else {
switch (v.expected) {
case 'string':
newMessage = t('zod.generic.validString', [
t(v.message),
]);
break;
case 'boolean':
newMessage = t('zod.generic.validBoolean', [
t(v.message),
]);
break;
}
}
break;
}
}
}
if (newMessage) {
m = newMessage;
} else {
m = t(v.message);
}
}
return m;

Loading…
Cancel
Save