B2B-88: add starter kit structure and elements

This commit is contained in:
devmc-ee
2025-06-08 16:18:30 +03:00
parent 657a36a298
commit e7b25600cb
1280 changed files with 77893 additions and 5688 deletions

View File

@@ -0,0 +1,10 @@
import { z } from 'zod';
import { RefinedPasswordSchema, refineRepeatPassword } from './password.schema';
export const PasswordResetSchema = z
.object({
password: RefinedPasswordSchema,
repeatPassword: RefinedPasswordSchema,
})
.superRefine(refineRepeatPassword);

View File

@@ -0,0 +1,8 @@
import { z } from 'zod';
import { PasswordSchema } from './password.schema';
export const PasswordSignInSchema = z.object({
email: z.string().email(),
password: PasswordSchema,
});

View File

@@ -0,0 +1,11 @@
import { z } from 'zod';
import { RefinedPasswordSchema, refineRepeatPassword } from './password.schema';
export const PasswordSignUpSchema = z
.object({
email: z.string().email(),
password: RefinedPasswordSchema,
repeatPassword: RefinedPasswordSchema,
})
.superRefine(refineRepeatPassword);

View File

@@ -0,0 +1,82 @@
import { z } from 'zod';
/**
* Password requirements
* These are the requirements for the password when signing up or changing the password
*/
const requirements = {
minLength: 6,
maxLength: 99,
specialChars:
process.env.NEXT_PUBLIC_PASSWORD_REQUIRE_SPECIAL_CHARS === 'true',
numbers: process.env.NEXT_PUBLIC_PASSWORD_REQUIRE_NUMBERS === 'true',
uppercase: process.env.NEXT_PUBLIC_PASSWORD_REQUIRE_UPPERCASE === 'true',
};
/**
* Password schema
* This is used to validate the password on sign in (for existing users when requirements are not enforced)
*/
export const PasswordSchema = z
.string()
.min(requirements.minLength)
.max(requirements.maxLength);
/**
* Refined password schema with additional requirements
* This is required to validate the password requirements on sign up and password change
*/
export const RefinedPasswordSchema = PasswordSchema.superRefine((val, ctx) =>
validatePassword(val, ctx),
);
export function refineRepeatPassword(
data: { password: string; repeatPassword: string },
ctx: z.RefinementCtx,
) {
if (data.password !== data.repeatPassword) {
ctx.addIssue({
message: 'auth:errors.passwordsDoNotMatch',
path: ['repeatPassword'],
code: 'custom',
});
}
return true;
}
function validatePassword(password: string, ctx: z.RefinementCtx) {
if (requirements.specialChars) {
const specialCharsCount =
password.match(/[!@#$%^&*(),.?":{}|<>]/g)?.length ?? 0;
if (specialCharsCount < 1) {
ctx.addIssue({
message: 'auth:errors.minPasswordSpecialChars',
code: 'custom',
});
}
}
if (requirements.numbers) {
const numbersCount = password.match(/\d/g)?.length ?? 0;
if (numbersCount < 1) {
ctx.addIssue({
message: 'auth:errors.minPasswordNumbers',
code: 'custom',
});
}
}
if (requirements.uppercase) {
if (!/[A-Z]/.test(password)) {
ctx.addIssue({
message: 'auth:errors.uppercasePassword',
code: 'custom',
});
}
}
return true;
}