Compare commits
6 commits
4b53ce906c
...
ea7ef2a779
Author | SHA1 | Date | |
---|---|---|---|
ea7ef2a779 | |||
0c529c3cb8 | |||
679c90cdbd | |||
93b603317a | |||
a29580708e | |||
d2a0f6125b |
12 changed files with 213 additions and 620 deletions
2
.github/workflows/container-scan.yml
vendored
2
.github/workflows/container-scan.yml
vendored
|
@ -9,7 +9,7 @@ jobs:
|
|||
name: Container Scan
|
||||
runs-on: docker
|
||||
container:
|
||||
image: ghcr.io/di0ik/forgejo_runner_container:main@sha256:c4667f2702c32b91b4c92db2ff20739edd00409a44a691c0598cf4a09a47743a
|
||||
image: ghcr.io/di0ik/forgejo_runner_container:main@sha256:c66a37d9af18f8f0f34d16890082bc08d842d52ff2a2bc36d993e3d347b498ac
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
|
|
2
.github/workflows/docker-build.yml
vendored
2
.github/workflows/docker-build.yml
vendored
|
@ -13,7 +13,7 @@ jobs:
|
|||
docker:
|
||||
runs-on: docker
|
||||
container:
|
||||
image: ghcr.io/di0ik/forgejo_runner_container:main@sha256:c4667f2702c32b91b4c92db2ff20739edd00409a44a691c0598cf4a09a47743a
|
||||
image: ghcr.io/di0ik/forgejo_runner_container:main@sha256:c66a37d9af18f8f0f34d16890082bc08d842d52ff2a2bc36d993e3d347b498ac
|
||||
steps:
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||
|
|
12
package.json
12
package.json
|
@ -22,7 +22,6 @@
|
|||
"@fortawesome/free-regular-svg-icons": "^6.7.2",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.7.2",
|
||||
"@fortawesome/react-fontawesome": "^0.2.2",
|
||||
"@hookform/resolvers": "^5.0.1",
|
||||
"@prisma/client": "^6.9.0",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.14",
|
||||
"@radix-ui/react-hover-card": "^1.1.13",
|
||||
|
@ -33,7 +32,6 @@
|
|||
"@radix-ui/react-slot": "^1.2.2",
|
||||
"@radix-ui/react-switch": "^1.2.4",
|
||||
"@radix-ui/react-tabs": "^1.1.11",
|
||||
"bcryptjs": "^3.0.2",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"date-fns": "^4.1.0",
|
||||
|
@ -45,22 +43,20 @@
|
|||
"react-big-calendar": "^1.18.0",
|
||||
"react-datepicker": "^8.4.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-hook-form": "^7.56.4",
|
||||
"tailwind-merge": "^3.2.0",
|
||||
"zod": "^3.25.60"
|
||||
"tailwind-merge": "^3.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "3.3.1",
|
||||
"@tailwindcss/postcss": "4.1.10",
|
||||
"@types/node": "22.15.32",
|
||||
"@types/node": "22.15.31",
|
||||
"@types/react": "19.1.8",
|
||||
"@types/react-big-calendar": "^1",
|
||||
"@types/react-dom": "19.1.6",
|
||||
"dotenv-cli": "8.0.0",
|
||||
"eslint": "9.29.0",
|
||||
"eslint": "9.28.0",
|
||||
"eslint-config-next": "15.3.3",
|
||||
"eslint-config-prettier": "10.1.5",
|
||||
"postcss": "8.5.6",
|
||||
"postcss": "8.5.5",
|
||||
"prettier": "3.5.3",
|
||||
"prisma": "6.9.0",
|
||||
"tailwindcss": "4.1.10",
|
||||
|
|
|
@ -6,6 +6,8 @@ import { Button } from '@/components/custom-ui/button';
|
|||
import Image from 'next/image';
|
||||
import { Separator } from '@/components/custom-ui/separator';
|
||||
import Logo from '@/components/logo';
|
||||
|
||||
import '@/app/globals.css';
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
|
@ -26,12 +28,12 @@ export default async function LoginPage() {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className='flex flex-col items-center min-h-screen'>
|
||||
<div className='flex flex-col items-center min-h-screen'>
|
||||
<div className='fixed top-4 right-4'>
|
||||
<div className='flex flex-col items-center justify-center h-screen'>
|
||||
<div className='flex flex-col items-center justify-center h-screen'>
|
||||
<div className='absolute top-4 right-4'>
|
||||
<ThemePicker />
|
||||
</div>
|
||||
<div className='mt-auto mb-auto'>
|
||||
<div>
|
||||
<Card className='w-[350px] max-w-screen;'>
|
||||
<CardHeader className='grid place-items-center'>
|
||||
<Logo colorType='colored' logoType='secondary'></Logo>
|
||||
|
@ -41,6 +43,8 @@ export default async function LoginPage() {
|
|||
|
||||
<Separator className='h-[1px] rounded-sm w-[60%] bg-border' />
|
||||
|
||||
{providerMap.length > 0}
|
||||
|
||||
{providerMap.map((provider) => (
|
||||
<SSOLogin
|
||||
key={provider.id}
|
||||
|
|
83
src/auth.ts
83
src/auth.ts
|
@ -1,88 +1,24 @@
|
|||
import NextAuth, { CredentialsSignin } from 'next-auth';
|
||||
import NextAuth from 'next-auth';
|
||||
|
||||
import { Prisma } from '@/generated/prisma';
|
||||
import type { Provider } from 'next-auth/providers';
|
||||
import Credentials from 'next-auth/providers/credentials';
|
||||
|
||||
import Authentik from 'next-auth/providers/authentik';
|
||||
|
||||
import { PrismaAdapter } from '@auth/prisma-adapter';
|
||||
import { prisma } from '@/prisma';
|
||||
|
||||
import { loginSchema } from './lib/validation/user';
|
||||
|
||||
import { ZodError } from 'zod';
|
||||
|
||||
class InvalidLoginError extends CredentialsSignin {
|
||||
constructor(code: string) {
|
||||
super();
|
||||
this.code = code;
|
||||
this.message = code;
|
||||
}
|
||||
}
|
||||
|
||||
const providers: Provider[] = [
|
||||
!process.env.DISABLE_PASSWORD_LOGIN &&
|
||||
Credentials({
|
||||
credentials: { password: { label: 'Password', type: 'password' } },
|
||||
async authorize(c) {
|
||||
if (process.env.NODE_ENV === 'development' && c.password === 'password')
|
||||
return {
|
||||
id: 'test',
|
||||
name: 'Test User',
|
||||
email: 'test@example.com',
|
||||
};
|
||||
if (process.env.DISABLE_PASSWORD_LOGIN) return null;
|
||||
|
||||
try {
|
||||
const { email, password } = await loginSchema.parseAsync(c);
|
||||
|
||||
const user = await prisma.user.findFirst({
|
||||
where: { email },
|
||||
include: { accounts: true },
|
||||
});
|
||||
|
||||
if (!user)
|
||||
throw new InvalidLoginError(
|
||||
'username/email or password is not correct',
|
||||
);
|
||||
|
||||
if (user.accounts[0].provider !== 'credentials') {
|
||||
throw new InvalidLoginError(
|
||||
'username/email or password is not correct',
|
||||
);
|
||||
}
|
||||
|
||||
const passwordsMatch = await (
|
||||
await import('bcryptjs')
|
||||
).compare(password, user.password_hash!);
|
||||
|
||||
if (!passwordsMatch) {
|
||||
throw new InvalidLoginError(
|
||||
'username/email or password is not correct',
|
||||
);
|
||||
}
|
||||
|
||||
if (!user.emailVerified) {
|
||||
throw new InvalidLoginError(
|
||||
'Email not verified. Please check your inbox.',
|
||||
);
|
||||
}
|
||||
|
||||
return user;
|
||||
} catch (error) {
|
||||
if (
|
||||
error instanceof Prisma?.PrismaClientInitializationError ||
|
||||
error instanceof Prisma?.PrismaClientKnownRequestError
|
||||
) {
|
||||
throw new InvalidLoginError('System error. Please contact support');
|
||||
}
|
||||
|
||||
if (error instanceof ZodError) {
|
||||
throw new InvalidLoginError(error.issues[0].message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
authorize(c) {
|
||||
if (c.password !== 'password') return null;
|
||||
return {
|
||||
id: 'test',
|
||||
name: 'Test User',
|
||||
email: 'test@example.com',
|
||||
};
|
||||
},
|
||||
}),
|
||||
process.env.AUTH_AUTHENTIK_ID && Authentik,
|
||||
|
@ -114,5 +50,4 @@ export const { handlers, signIn, signOut, auth } = NextAuth({
|
|||
return !!auth?.user;
|
||||
},
|
||||
},
|
||||
debug: process.env.NODE_ENV === 'development',
|
||||
});
|
||||
|
|
|
@ -8,8 +8,6 @@ export default function LabeledInput({
|
|||
value,
|
||||
name,
|
||||
autocomplete,
|
||||
error,
|
||||
...rest
|
||||
}: {
|
||||
type: 'text' | 'email' | 'password';
|
||||
label: string;
|
||||
|
@ -17,8 +15,7 @@ export default function LabeledInput({
|
|||
value?: string;
|
||||
name?: string;
|
||||
autocomplete?: string;
|
||||
error?: string;
|
||||
} & React.InputHTMLAttributes<HTMLInputElement>) {
|
||||
}) {
|
||||
return (
|
||||
<div className='grid grid-cols-1 gap-1'>
|
||||
<Label htmlFor={name}>{label}</Label>
|
||||
|
@ -30,9 +27,7 @@ export default function LabeledInput({
|
|||
id={name}
|
||||
name={name}
|
||||
autoComplete={autocomplete}
|
||||
{...rest}
|
||||
/>
|
||||
{error && <p className='text-red-500 text-sm mt-1'>{error}</p>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,214 +1,107 @@
|
|||
'use client';
|
||||
|
||||
import React, { useState, useRef } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
import { signIn } from '@/auth';
|
||||
import LabeledInput from '@/components/labeled-input';
|
||||
import { Button } from '@/components/custom-ui/button';
|
||||
import useZodForm from '@/lib/hooks/useZodForm';
|
||||
import { loginSchema, registerSchema } from '@/lib/validation/user';
|
||||
import { loginAction } from '@/lib/auth/login';
|
||||
import { registerAction } from '@/lib/auth/register';
|
||||
import { AuthError } from 'next-auth';
|
||||
import { redirect } from 'next/navigation';
|
||||
import { useRef, useState } from 'react';
|
||||
|
||||
function LoginFormElement({
|
||||
setIsSignUp,
|
||||
formRef,
|
||||
}: {
|
||||
setIsSignUp: (value: boolean | ((prev: boolean) => boolean)) => void;
|
||||
formRef?: React.RefObject<HTMLFormElement | null>;
|
||||
}) {
|
||||
const { handleSubmit, formState, register, setError } =
|
||||
useZodForm(loginSchema);
|
||||
const router = useRouter();
|
||||
|
||||
const onSubmit = handleSubmit(async (data) => {
|
||||
try {
|
||||
const { error } = await loginAction(data);
|
||||
|
||||
if (error) {
|
||||
setError('root', {
|
||||
message: error,
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
router.push('/home');
|
||||
router.refresh();
|
||||
return;
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof Error)
|
||||
setError('root', {
|
||||
message: error?.message,
|
||||
});
|
||||
else
|
||||
setError('root', {
|
||||
message: 'An unknown error occurred.',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<form className='flex flex-col gap-5 w-full' onSubmit={onSubmit}>
|
||||
<LabeledInput
|
||||
type='text'
|
||||
label='E-Mail or Username'
|
||||
placeholder='What you are known as'
|
||||
error={formState.errors.email?.message}
|
||||
{...register('email')}
|
||||
/>
|
||||
<LabeledInput
|
||||
type='password'
|
||||
label='Password'
|
||||
placeholder="Let's hope you remember it"
|
||||
error={formState.errors.password?.message}
|
||||
{...register('password')}
|
||||
/>
|
||||
<div className='grid grid-rows-2 gap-2'>
|
||||
<Button type='submit' variant='primary'>
|
||||
Login
|
||||
</Button>
|
||||
<Button
|
||||
type='button'
|
||||
variant='outline_primary'
|
||||
onClick={() => {
|
||||
formRef?.current?.reset();
|
||||
setIsSignUp((v) => !v);
|
||||
}}
|
||||
>
|
||||
Sign Up
|
||||
</Button>
|
||||
</div>
|
||||
<div>
|
||||
{formState.errors.root?.message && (
|
||||
<p className='text-red-500'>{formState.errors.root?.message}</p>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
function RegisterFormElement({
|
||||
setIsSignUp,
|
||||
formRef,
|
||||
}: {
|
||||
setIsSignUp: (value: boolean | ((prev: boolean) => boolean)) => void;
|
||||
formRef?: React.RefObject<HTMLFormElement | null>;
|
||||
}) {
|
||||
const { handleSubmit, formState, register, setError } =
|
||||
useZodForm(registerSchema);
|
||||
|
||||
const onSubmit = handleSubmit(async (data) => {
|
||||
try {
|
||||
const { error } = await registerAction(data);
|
||||
|
||||
if (error) {
|
||||
setError('root', {
|
||||
message: error,
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
formRef?.current?.reset();
|
||||
setIsSignUp(false);
|
||||
// TODO: Show registration success message (reminder to verify email)
|
||||
return;
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof Error)
|
||||
setError('root', {
|
||||
message: error?.message,
|
||||
});
|
||||
else
|
||||
setError('root', {
|
||||
message: 'An unknown error occurred.',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<form
|
||||
ref={formRef}
|
||||
className='flex flex-col gap-5 w-full'
|
||||
onSubmit={onSubmit}
|
||||
>
|
||||
<LabeledInput
|
||||
type='text'
|
||||
label='First Name'
|
||||
placeholder='Your first name'
|
||||
autocomplete='given-name'
|
||||
error={formState.errors.firstName?.message}
|
||||
{...register('firstName')}
|
||||
/>
|
||||
<LabeledInput
|
||||
type='text'
|
||||
label='Last Name'
|
||||
placeholder='Your last name'
|
||||
autocomplete='family-name'
|
||||
error={formState.errors.lastName?.message}
|
||||
{...register('lastName')}
|
||||
/>
|
||||
<LabeledInput
|
||||
type='email'
|
||||
label='E-Mail'
|
||||
placeholder='Your email address'
|
||||
autocomplete='email'
|
||||
error={formState.errors.email?.message}
|
||||
{...register('email')}
|
||||
/>
|
||||
<LabeledInput
|
||||
type='text'
|
||||
label='Username'
|
||||
placeholder='Your username'
|
||||
autocomplete='username'
|
||||
error={formState.errors.username?.message}
|
||||
{...register('username')}
|
||||
/>
|
||||
<LabeledInput
|
||||
type='password'
|
||||
label='Password'
|
||||
placeholder='Create a password'
|
||||
autocomplete='new-password'
|
||||
error={formState.errors.password?.message}
|
||||
{...register('password')}
|
||||
/>
|
||||
<LabeledInput
|
||||
type='password'
|
||||
label='Confirm Password'
|
||||
placeholder='Repeat your password'
|
||||
autocomplete='new-password'
|
||||
error={formState.errors.confirmPassword?.message}
|
||||
{...register('confirmPassword')}
|
||||
/>
|
||||
<div className='grid grid-rows-2 gap-2'>
|
||||
<Button type='submit' variant='primary'>
|
||||
Sign Up
|
||||
</Button>
|
||||
<Button
|
||||
type='button'
|
||||
variant='outline_primary'
|
||||
onClick={() => {
|
||||
formRef?.current?.reset();
|
||||
setIsSignUp((v) => !v);
|
||||
}}
|
||||
>
|
||||
Back to Login
|
||||
</Button>
|
||||
</div>
|
||||
<div>
|
||||
{formState.errors.root?.message && (
|
||||
<p className='text-red-500'>{formState.errors.root?.message}</p>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
const SIGNIN_ERROR_URL = '/error';
|
||||
|
||||
export default function LoginForm() {
|
||||
const [isSignUp, setIsSignUp] = useState(false);
|
||||
|
||||
const formRef = useRef<HTMLFormElement>(null);
|
||||
|
||||
if (isSignUp) {
|
||||
return <RegisterFormElement setIsSignUp={setIsSignUp} formRef={formRef} />;
|
||||
}
|
||||
return <LoginFormElement setIsSignUp={setIsSignUp} formRef={formRef} />;
|
||||
return (
|
||||
<form
|
||||
ref={formRef}
|
||||
className='flex flex-col gap-5 w-full'
|
||||
action={async (formData) => {
|
||||
'use client';
|
||||
try {
|
||||
if (isSignUp) {
|
||||
// handle sign up logic here
|
||||
} else {
|
||||
await signIn('credentials', formData);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof AuthError) {
|
||||
return redirect(`${SIGNIN_ERROR_URL}?error=${error.type}`);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}}
|
||||
>
|
||||
{isSignUp ? (
|
||||
<>
|
||||
<LabeledInput
|
||||
type='text'
|
||||
label='First Name'
|
||||
placeholder='Your first name'
|
||||
name='firstName'
|
||||
autocomplete='given-name'
|
||||
/>
|
||||
<LabeledInput
|
||||
type='text'
|
||||
label='Last Name'
|
||||
placeholder='Your last name'
|
||||
name='lastName'
|
||||
autocomplete='family-name'
|
||||
/>
|
||||
<LabeledInput
|
||||
type='email'
|
||||
label='E-Mail'
|
||||
placeholder='Your email address'
|
||||
name='email'
|
||||
autocomplete='email'
|
||||
/>
|
||||
<LabeledInput
|
||||
type='password'
|
||||
label='Password'
|
||||
placeholder='Create a password'
|
||||
name='password'
|
||||
autocomplete='new-password'
|
||||
/>
|
||||
<LabeledInput
|
||||
type='password'
|
||||
label='Confirm Password'
|
||||
placeholder='Repeat your password'
|
||||
name='confirmPassword'
|
||||
autocomplete='new-password'
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<LabeledInput
|
||||
type='email'
|
||||
label='E-Mail or Username'
|
||||
placeholder='What you are known as'
|
||||
name='email'
|
||||
/>
|
||||
<LabeledInput
|
||||
type='password'
|
||||
label='Password'
|
||||
placeholder="Let's hope you remember it"
|
||||
name='password'
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<div className='grid grid-rows-2 gap-2'>
|
||||
<Button type='submit' variant='primary'>
|
||||
{isSignUp ? 'Sign Up' : 'Login'}
|
||||
</Button>
|
||||
<Button
|
||||
type='button'
|
||||
variant='outline_primary'
|
||||
onClick={() => {
|
||||
formRef.current?.reset();
|
||||
setIsSignUp((v) => !v);
|
||||
}}
|
||||
>
|
||||
{isSignUp ? 'Back to Login' : 'Sign Up'}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
'use server';
|
||||
|
||||
import { z } from 'zod';
|
||||
import { loginSchema } from '@/lib/validation/user';
|
||||
import { signIn } from '@/auth';
|
||||
|
||||
export async function loginAction(data: z.infer<typeof loginSchema>) {
|
||||
try {
|
||||
await signIn('credentials', {
|
||||
...data,
|
||||
redirect: false,
|
||||
});
|
||||
|
||||
return {
|
||||
error: undefined,
|
||||
};
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof Error) {
|
||||
return {
|
||||
error: error.message.toString(),
|
||||
};
|
||||
}
|
||||
return {
|
||||
error: 'An unknown error occurred.',
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
'use server';
|
||||
|
||||
import type { z } from 'zod';
|
||||
import bcrypt from 'bcryptjs';
|
||||
import { registerSchema } from '@/lib/validation/user';
|
||||
import { prisma } from '@/prisma';
|
||||
|
||||
export async function registerAction(data: z.infer<typeof registerSchema>) {
|
||||
try {
|
||||
const result = await registerSchema.safeParseAsync(data);
|
||||
|
||||
if (!result.success) {
|
||||
return {
|
||||
error: result.error.errors[0].message,
|
||||
};
|
||||
}
|
||||
|
||||
const { email, password, firstName, lastName, username } = result.data;
|
||||
|
||||
const user = await prisma.user.findUnique({
|
||||
where: {
|
||||
email,
|
||||
},
|
||||
});
|
||||
|
||||
if (user) {
|
||||
return {
|
||||
error: 'User already exist with this email',
|
||||
};
|
||||
}
|
||||
|
||||
const existingUsername = await prisma.user.findUnique({
|
||||
where: {
|
||||
name: username,
|
||||
},
|
||||
});
|
||||
|
||||
if (existingUsername) {
|
||||
return {
|
||||
error: 'Username already exists',
|
||||
};
|
||||
}
|
||||
|
||||
const passwordHash = await bcrypt.hash(password, 10);
|
||||
|
||||
await prisma.$transaction(async (tx) => {
|
||||
const { id } = await tx.user.create({
|
||||
data: {
|
||||
email,
|
||||
name: username,
|
||||
password_hash: passwordHash,
|
||||
first_name: firstName,
|
||||
last_name: lastName,
|
||||
emailVerified: new Date(), // TODO: handle email verification
|
||||
},
|
||||
});
|
||||
|
||||
await tx.account.create({
|
||||
data: {
|
||||
userId: id,
|
||||
type: 'credentials',
|
||||
provider: 'credentials',
|
||||
providerAccountId: id,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return {};
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
} catch (_error) {
|
||||
return {
|
||||
error: 'System error. Please contact support',
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
|
||||
export default function useZodForm<
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
Schema extends z.ZodType<any, any, any>,
|
||||
Values extends z.infer<Schema>,
|
||||
>(schema: Schema, defaultValues?: Values) {
|
||||
return useForm<Values>({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues,
|
||||
});
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
import zod from 'zod';
|
||||
|
||||
export const loginSchema = zod.object({
|
||||
email: zod
|
||||
.string()
|
||||
.email('Invalid email address')
|
||||
.min(3, 'Email is required')
|
||||
.or(
|
||||
zod
|
||||
.string()
|
||||
.min(3, 'Username is required')
|
||||
.max(32, 'Username must be at most 32 characters long')
|
||||
.regex(
|
||||
/^[a-zA-Z0-9_]+$/,
|
||||
'Username can only contain letters, numbers, and underscores',
|
||||
),
|
||||
),
|
||||
password: zod.string().min(1, 'Password is required'),
|
||||
});
|
||||
|
||||
export const registerSchema = zod
|
||||
.object({
|
||||
firstName: zod
|
||||
.string()
|
||||
.min(1, 'First name is required')
|
||||
.max(32, 'First name must be at most 32 characters long'),
|
||||
lastName: zod
|
||||
.string()
|
||||
.min(1, 'Last name is required')
|
||||
.max(32, 'Last name must be at most 32 characters long'),
|
||||
email: zod
|
||||
.string()
|
||||
.email('Invalid email address')
|
||||
.min(3, 'Email is required'),
|
||||
password: zod
|
||||
.string()
|
||||
.min(8, 'Password must be at least 8 characters long')
|
||||
.max(128, 'Password must be at most 128 characters long'),
|
||||
confirmPassword: zod
|
||||
.string()
|
||||
.min(8, 'Password must be at least 8 characters long')
|
||||
.max(128, 'Password must be at most 128 characters long'),
|
||||
username: zod
|
||||
.string()
|
||||
.min(3, 'Username is required')
|
||||
.max(32, 'Username must be at most 32 characters long')
|
||||
.regex(
|
||||
/^[a-zA-Z0-9_]+$/,
|
||||
'Username can only contain letters, numbers, and underscores',
|
||||
),
|
||||
})
|
||||
.refine((data) => data.password === data.confirmPassword, {
|
||||
message: 'Passwords do not match',
|
||||
path: ['confirmPassword'],
|
||||
})
|
||||
.refine(
|
||||
(data) =>
|
||||
!data.password.includes(data.firstName) &&
|
||||
!data.password.includes(data.lastName) &&
|
||||
!data.password.includes(data.email) &&
|
||||
!data.password.includes(data.username),
|
||||
{
|
||||
message:
|
||||
'Password cannot contain your first name, last name, email, or username',
|
||||
path: ['password'],
|
||||
},
|
||||
);
|
233
yarn.lock
233
yarn.lock
|
@ -110,7 +110,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@eslint/config-array@npm:^0.20.1":
|
||||
"@eslint/config-array@npm:^0.20.0":
|
||||
version: 0.20.1
|
||||
resolution: "@eslint/config-array@npm:0.20.1"
|
||||
dependencies:
|
||||
|
@ -163,10 +163,10 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@eslint/js@npm:9.29.0":
|
||||
version: 9.29.0
|
||||
resolution: "@eslint/js@npm:9.29.0"
|
||||
checksum: 10c0/d0ccf37063fa27a3fae9347cb044f84ca10b5a2fa19ffb2b3fedf3b96843ac1ff359ea9f0ab0e80f2f16fda4cb0dc61ea0fed0375090f050fe0a029e7d6de3a3
|
||||
"@eslint/js@npm:9.28.0":
|
||||
version: 9.28.0
|
||||
resolution: "@eslint/js@npm:9.28.0"
|
||||
checksum: 10c0/5a6759542490dd9f778993edfbc8d2f55168fd0f7336ceed20fe3870c65499d72fc0bca8d1ae00ea246b0923ea4cba2e0758a8a5507a3506ddcf41c92282abb8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -294,17 +294,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@hookform/resolvers@npm:^5.0.1":
|
||||
version: 5.1.1
|
||||
resolution: "@hookform/resolvers@npm:5.1.1"
|
||||
dependencies:
|
||||
"@standard-schema/utils": "npm:^0.3.0"
|
||||
peerDependencies:
|
||||
react-hook-form: ^7.55.0
|
||||
checksum: 10c0/74601ba4abb3159bbaa25175af9459a2c0337a28d8c0a5be95c7ae7b0a76ddafcf63c03eea8561fd099fe80b226194ad09e3824c53b9beda38393ff9fd264a03
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@humanfs/core@npm:^0.19.1":
|
||||
version: 0.19.1
|
||||
resolution: "@humanfs/core@npm:0.19.1"
|
||||
|
@ -1488,13 +1477,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@standard-schema/utils@npm:^0.3.0":
|
||||
version: 0.3.0
|
||||
resolution: "@standard-schema/utils@npm:0.3.0"
|
||||
checksum: 10c0/6eb74cd13e52d5fc74054df51e37d947ef53f3ab9e02c085665dcca3c38c60ece8d735cebbdf18fbb13c775fbcb9becb3f53109b0e092a63f0f7389ce0993fd0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@swc/counter@npm:0.1.3":
|
||||
version: 0.1.3
|
||||
resolution: "@swc/counter@npm:0.1.3"
|
||||
|
@ -1714,12 +1696,12 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/node@npm:22.15.32":
|
||||
version: 22.15.32
|
||||
resolution: "@types/node@npm:22.15.32"
|
||||
"@types/node@npm:22.15.31":
|
||||
version: 22.15.31
|
||||
resolution: "@types/node@npm:22.15.31"
|
||||
dependencies:
|
||||
undici-types: "npm:~6.21.0"
|
||||
checksum: 10c0/63a2fa52adf1134d1b3bee8b1862d4b8e4550fffc190551068d3d41a41d9e5c0c8f1cb81faa18767b260637360f662115c26c5e4e7718868ead40c4a57cbc0e3
|
||||
checksum: 10c0/ef7d5dc890da41cfd554d35ab8998bc18be9e3a0caa642e720599ac4410a94a4879766e52b3c9cafa06c66b7b8aebdc51f322cf67df23a6489927890196a316d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -1767,104 +1749,104 @@ __metadata:
|
|||
linkType: hard
|
||||
|
||||
"@typescript-eslint/eslint-plugin@npm:^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0":
|
||||
version: 8.34.1
|
||||
resolution: "@typescript-eslint/eslint-plugin@npm:8.34.1"
|
||||
version: 8.34.0
|
||||
resolution: "@typescript-eslint/eslint-plugin@npm:8.34.0"
|
||||
dependencies:
|
||||
"@eslint-community/regexpp": "npm:^4.10.0"
|
||||
"@typescript-eslint/scope-manager": "npm:8.34.1"
|
||||
"@typescript-eslint/type-utils": "npm:8.34.1"
|
||||
"@typescript-eslint/utils": "npm:8.34.1"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.34.1"
|
||||
"@typescript-eslint/scope-manager": "npm:8.34.0"
|
||||
"@typescript-eslint/type-utils": "npm:8.34.0"
|
||||
"@typescript-eslint/utils": "npm:8.34.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.34.0"
|
||||
graphemer: "npm:^1.4.0"
|
||||
ignore: "npm:^7.0.0"
|
||||
natural-compare: "npm:^1.4.0"
|
||||
ts-api-utils: "npm:^2.1.0"
|
||||
peerDependencies:
|
||||
"@typescript-eslint/parser": ^8.34.1
|
||||
"@typescript-eslint/parser": ^8.34.0
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
typescript: ">=4.8.4 <5.9.0"
|
||||
checksum: 10c0/f1c9f25e4fe4b59622312dfa0ca1e80fa7945296ba5c04362a5fda084a17e23a6b98dac331f5a13bcb1ba34a2b598a3f5c41aa288f0c51fe60196e912954e56a
|
||||
checksum: 10c0/905a05d15f4b0367838ec445f9890321d87470198bf7a589278fc0f38c82cf3ccc1efce4acd3c9c94ee6149d5579ef58606fb7c50f4db50c830de65af8c27c6d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/parser@npm:^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0":
|
||||
version: 8.34.1
|
||||
resolution: "@typescript-eslint/parser@npm:8.34.1"
|
||||
version: 8.34.0
|
||||
resolution: "@typescript-eslint/parser@npm:8.34.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager": "npm:8.34.1"
|
||||
"@typescript-eslint/types": "npm:8.34.1"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.34.1"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.34.1"
|
||||
"@typescript-eslint/scope-manager": "npm:8.34.0"
|
||||
"@typescript-eslint/types": "npm:8.34.0"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.34.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.34.0"
|
||||
debug: "npm:^4.3.4"
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
typescript: ">=4.8.4 <5.9.0"
|
||||
checksum: 10c0/bf8070245d53ef6926ff6630bb72f245923f545304e2a61508fb944802a83fed8eab961d9010956d07999d51afdfbbec82aea9d6185295551a7c17c00d759183
|
||||
checksum: 10c0/a829be00ea3455c1e50983c8b44476fbfc9329d019764e407c4d591a95dbd168f83f13e309751242bb4fdc02f89cb51ca5cdc912a12b10f69eebcb1c46dcc39b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/project-service@npm:8.34.1":
|
||||
version: 8.34.1
|
||||
resolution: "@typescript-eslint/project-service@npm:8.34.1"
|
||||
"@typescript-eslint/project-service@npm:8.34.0":
|
||||
version: 8.34.0
|
||||
resolution: "@typescript-eslint/project-service@npm:8.34.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/tsconfig-utils": "npm:^8.34.1"
|
||||
"@typescript-eslint/types": "npm:^8.34.1"
|
||||
"@typescript-eslint/tsconfig-utils": "npm:^8.34.0"
|
||||
"@typescript-eslint/types": "npm:^8.34.0"
|
||||
debug: "npm:^4.3.4"
|
||||
peerDependencies:
|
||||
typescript: ">=4.8.4 <5.9.0"
|
||||
checksum: 10c0/9333a890625f6777054db17a6b299281ae7502bb7615261d15b885a75b8cf65fc91591389c93b37ecd14b651d8e94851dac8718e5dcc8ed0600533535dae855c
|
||||
checksum: 10c0/88e64b8daf7db9603277fcbeb9e585e70ec6d6e34fa10d4b60f421e48081cc7c1f6acb01e1ee9dd95e10c0601f164c1defbfe6c9d1edc9822089bb72dbb0fc80
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/scope-manager@npm:8.34.1":
|
||||
version: 8.34.1
|
||||
resolution: "@typescript-eslint/scope-manager@npm:8.34.1"
|
||||
"@typescript-eslint/scope-manager@npm:8.34.0":
|
||||
version: 8.34.0
|
||||
resolution: "@typescript-eslint/scope-manager@npm:8.34.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": "npm:8.34.1"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.34.1"
|
||||
checksum: 10c0/2af608fa3900f4726322e33bf4f3a376fdace3ac0f310cf7d9256bbc2905c3896138176a47dd195d2c2229f27fe43f5deb4bc7729db2eb18389926dedea78077
|
||||
"@typescript-eslint/types": "npm:8.34.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.34.0"
|
||||
checksum: 10c0/35af36bddc4c227cb0bac42192c40b38179ced30866b6aac642781e21c3f3b1c72051eb4f685d7c99517c3296dd6ba83dd8360e4072e8dcf604aae266eece1b4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/tsconfig-utils@npm:8.34.1, @typescript-eslint/tsconfig-utils@npm:^8.34.1":
|
||||
version: 8.34.1
|
||||
resolution: "@typescript-eslint/tsconfig-utils@npm:8.34.1"
|
||||
"@typescript-eslint/tsconfig-utils@npm:8.34.0, @typescript-eslint/tsconfig-utils@npm:^8.34.0":
|
||||
version: 8.34.0
|
||||
resolution: "@typescript-eslint/tsconfig-utils@npm:8.34.0"
|
||||
peerDependencies:
|
||||
typescript: ">=4.8.4 <5.9.0"
|
||||
checksum: 10c0/8d1ead8b7c279b48e2ed96f083ec119a9aeea1ca9cdd40576ec271b996b9fd8cfa0ddb0aafbb4e14bc27fc62c69c5be66d39b1de68eab9ddd7f1861da267423d
|
||||
checksum: 10c0/98246f89d169d3feb453a6a8552c51d10225cb00c4ff1501549b7846e564ad0e218b644cd94ce779dceed07dcb9035c53fd32186b4c0223b7b2a1f7295b120c3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/type-utils@npm:8.34.1":
|
||||
version: 8.34.1
|
||||
resolution: "@typescript-eslint/type-utils@npm:8.34.1"
|
||||
"@typescript-eslint/type-utils@npm:8.34.0":
|
||||
version: 8.34.0
|
||||
resolution: "@typescript-eslint/type-utils@npm:8.34.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/typescript-estree": "npm:8.34.1"
|
||||
"@typescript-eslint/utils": "npm:8.34.1"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.34.0"
|
||||
"@typescript-eslint/utils": "npm:8.34.0"
|
||||
debug: "npm:^4.3.4"
|
||||
ts-api-utils: "npm:^2.1.0"
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
typescript: ">=4.8.4 <5.9.0"
|
||||
checksum: 10c0/502a2cdfe47f1f34206c747b5a70e0242dd99f570511db3dda9c5f999d9abadfbbb1dfa82a1fa437a1689d232715412e61c97d95f19c9314ba5ad23196b4096d
|
||||
checksum: 10c0/7c25d7f4186411190142390467160e81384d400cfb21183d8a305991c723da0a74e5528cdce30b5f2cb6d9d2f6af7c0981c20c18b45fc084b35632429270ae80
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/types@npm:8.34.1, @typescript-eslint/types@npm:^8.34.1":
|
||||
version: 8.34.1
|
||||
resolution: "@typescript-eslint/types@npm:8.34.1"
|
||||
checksum: 10c0/db1b3dce6a70b28ddb13c76fbb5983240d9395656df5f7cbd99bfd9905e39c0dab2132870f01dbc406b48739c437f7d344a879a824cedaba81b91a53110dc23a
|
||||
"@typescript-eslint/types@npm:8.34.0, @typescript-eslint/types@npm:^8.34.0":
|
||||
version: 8.34.0
|
||||
resolution: "@typescript-eslint/types@npm:8.34.0"
|
||||
checksum: 10c0/5d32b2ac03e4cbc1ac1777a53ee83d6d7887a783363bab4f0a6f7550a9e9df0254971cdf71e13b988e2215f2939e7592404856b8acb086ec63c4479c0225c742
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/typescript-estree@npm:8.34.1":
|
||||
version: 8.34.1
|
||||
resolution: "@typescript-eslint/typescript-estree@npm:8.34.1"
|
||||
"@typescript-eslint/typescript-estree@npm:8.34.0":
|
||||
version: 8.34.0
|
||||
resolution: "@typescript-eslint/typescript-estree@npm:8.34.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/project-service": "npm:8.34.1"
|
||||
"@typescript-eslint/tsconfig-utils": "npm:8.34.1"
|
||||
"@typescript-eslint/types": "npm:8.34.1"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.34.1"
|
||||
"@typescript-eslint/project-service": "npm:8.34.0"
|
||||
"@typescript-eslint/tsconfig-utils": "npm:8.34.0"
|
||||
"@typescript-eslint/types": "npm:8.34.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.34.0"
|
||||
debug: "npm:^4.3.4"
|
||||
fast-glob: "npm:^3.3.2"
|
||||
is-glob: "npm:^4.0.3"
|
||||
|
@ -1873,32 +1855,32 @@ __metadata:
|
|||
ts-api-utils: "npm:^2.1.0"
|
||||
peerDependencies:
|
||||
typescript: ">=4.8.4 <5.9.0"
|
||||
checksum: 10c0/4ee7249db91b9840361f34f80b7b6d646a3af159c7298d79a33d8a11c98792fd3a395343e5e17e0fa29529e8f0113bac8baadcef90d1e140bd736a48f0485042
|
||||
checksum: 10c0/e678982b0009e895aee2b4ccc55bb9ea5473a32e846a97c63d0c6a978c72e1a29e506e6a5f9dda45e9b7803e6c3e3abcdf4c316af1c59146abef4e10e0e94129
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/utils@npm:8.34.1":
|
||||
version: 8.34.1
|
||||
resolution: "@typescript-eslint/utils@npm:8.34.1"
|
||||
"@typescript-eslint/utils@npm:8.34.0":
|
||||
version: 8.34.0
|
||||
resolution: "@typescript-eslint/utils@npm:8.34.0"
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils": "npm:^4.7.0"
|
||||
"@typescript-eslint/scope-manager": "npm:8.34.1"
|
||||
"@typescript-eslint/types": "npm:8.34.1"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.34.1"
|
||||
"@typescript-eslint/scope-manager": "npm:8.34.0"
|
||||
"@typescript-eslint/types": "npm:8.34.0"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.34.0"
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
typescript: ">=4.8.4 <5.9.0"
|
||||
checksum: 10c0/e3085877f7940c02a37653e6bc52ac6cde115e755b1f788fe4331202f371b3421cc4d0878c7d3eb054e14e9b3a064496a707a73eac471cb2b73593b9e9d4b998
|
||||
checksum: 10c0/d759cf6f1b1b23d7d8ab922345e7b68b7c829f4bad841164312cfa3a3e8e818b962dd0d96c1aca7fd7c10248d56538d9714df5f3cfec9f159ca0a139feac60b9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/visitor-keys@npm:8.34.1":
|
||||
version: 8.34.1
|
||||
resolution: "@typescript-eslint/visitor-keys@npm:8.34.1"
|
||||
"@typescript-eslint/visitor-keys@npm:8.34.0":
|
||||
version: 8.34.0
|
||||
resolution: "@typescript-eslint/visitor-keys@npm:8.34.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": "npm:8.34.1"
|
||||
eslint-visitor-keys: "npm:^4.2.1"
|
||||
checksum: 10c0/0e5a9b3d93905d16d3cf8cb5fb346dcc6f760482eb7d0ac209aefc09a32f78ef28a687634df6ad08e81fb3e1083e8805f34472de6bbc501c0105ad654d518f40
|
||||
"@typescript-eslint/types": "npm:8.34.0"
|
||||
eslint-visitor-keys: "npm:^4.2.0"
|
||||
checksum: 10c0/d50997e921a178589913d08ffe14d02eba40666c90bdc0c9751f2b87ce500598f64027e2d866dfc975647b2f8b907158503d0722d6b1976c8f1cf5dd8e1d6d69
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -2250,15 +2232,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"bcryptjs@npm:^3.0.2":
|
||||
version: 3.0.2
|
||||
resolution: "bcryptjs@npm:3.0.2"
|
||||
bin:
|
||||
bcrypt: bin/bcrypt
|
||||
checksum: 10c0/a0923cac99f83e913f8f4e4f42df6a27c6593b24d509900331d1280c4050b1544e602a0ac67b43f7bb5c969991c3ed77fd72f19b7dc873be8ee794da3d925c7e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"brace-expansion@npm:^1.1.7":
|
||||
version: 1.1.12
|
||||
resolution: "brace-expansion@npm:1.1.12"
|
||||
|
@ -2336,9 +2309,9 @@ __metadata:
|
|||
linkType: hard
|
||||
|
||||
"caniuse-lite@npm:^1.0.30001579":
|
||||
version: 1.0.30001723
|
||||
resolution: "caniuse-lite@npm:1.0.30001723"
|
||||
checksum: 10c0/e019503061759b96017c4d27ddd7ca1b48533eabcd0431b51d2e3156f99f6b031075e46c279c0db63424cdfc874bba992caec2db51b922a0f945e686246886f6
|
||||
version: 1.0.30001722
|
||||
resolution: "caniuse-lite@npm:1.0.30001722"
|
||||
checksum: 10c0/a1e344c392e0b138f0b215525108877d725665217a5e8e7504897e30379a5a9b858bc44799ccc0e19f4a64bf1e05c15b4a58eb1c9032293f894aa24e8d9f470f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -2978,7 +2951,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"eslint-scope@npm:^8.4.0":
|
||||
"eslint-scope@npm:^8.3.0":
|
||||
version: 8.4.0
|
||||
resolution: "eslint-scope@npm:8.4.0"
|
||||
dependencies:
|
||||
|
@ -2995,24 +2968,24 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"eslint-visitor-keys@npm:^4.2.1":
|
||||
"eslint-visitor-keys@npm:^4.2.0, eslint-visitor-keys@npm:^4.2.1":
|
||||
version: 4.2.1
|
||||
resolution: "eslint-visitor-keys@npm:4.2.1"
|
||||
checksum: 10c0/fcd43999199d6740db26c58dbe0c2594623e31ca307e616ac05153c9272f12f1364f5a0b1917a8e962268fdecc6f3622c1c2908b4fcc2e047a106fe6de69dc43
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"eslint@npm:9.29.0":
|
||||
version: 9.29.0
|
||||
resolution: "eslint@npm:9.29.0"
|
||||
"eslint@npm:9.28.0":
|
||||
version: 9.28.0
|
||||
resolution: "eslint@npm:9.28.0"
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils": "npm:^4.2.0"
|
||||
"@eslint-community/regexpp": "npm:^4.12.1"
|
||||
"@eslint/config-array": "npm:^0.20.1"
|
||||
"@eslint/config-array": "npm:^0.20.0"
|
||||
"@eslint/config-helpers": "npm:^0.2.1"
|
||||
"@eslint/core": "npm:^0.14.0"
|
||||
"@eslint/eslintrc": "npm:^3.3.1"
|
||||
"@eslint/js": "npm:9.29.0"
|
||||
"@eslint/js": "npm:9.28.0"
|
||||
"@eslint/plugin-kit": "npm:^0.3.1"
|
||||
"@humanfs/node": "npm:^0.16.6"
|
||||
"@humanwhocodes/module-importer": "npm:^1.0.1"
|
||||
|
@ -3024,9 +2997,9 @@ __metadata:
|
|||
cross-spawn: "npm:^7.0.6"
|
||||
debug: "npm:^4.3.2"
|
||||
escape-string-regexp: "npm:^4.0.0"
|
||||
eslint-scope: "npm:^8.4.0"
|
||||
eslint-visitor-keys: "npm:^4.2.1"
|
||||
espree: "npm:^10.4.0"
|
||||
eslint-scope: "npm:^8.3.0"
|
||||
eslint-visitor-keys: "npm:^4.2.0"
|
||||
espree: "npm:^10.3.0"
|
||||
esquery: "npm:^1.5.0"
|
||||
esutils: "npm:^2.0.2"
|
||||
fast-deep-equal: "npm:^3.1.3"
|
||||
|
@ -3048,11 +3021,11 @@ __metadata:
|
|||
optional: true
|
||||
bin:
|
||||
eslint: bin/eslint.js
|
||||
checksum: 10c0/75e3f841e0f8b0fa93dbb2ba6ae538bd8b611c3654117bc3dadf90bb009923dfd2c15ec2948dc6e6b8b571317cc125c5cceb9255da8cd644ee740020df645dd8
|
||||
checksum: 10c0/513ea7e69d88a0905d4ed35cef3a8f31ebce7ca9f2cdbda3474495c63ad6831d52357aad65094be7a144d6e51850980ced7d25efb807e8ab06a427241f7cd730
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"espree@npm:^10.0.1, espree@npm:^10.4.0":
|
||||
"espree@npm:^10.0.1, espree@npm:^10.3.0":
|
||||
version: 10.4.0
|
||||
resolution: "espree@npm:10.4.0"
|
||||
dependencies:
|
||||
|
@ -4054,7 +4027,6 @@ __metadata:
|
|||
"@fortawesome/free-regular-svg-icons": "npm:^6.7.2"
|
||||
"@fortawesome/free-solid-svg-icons": "npm:^6.7.2"
|
||||
"@fortawesome/react-fontawesome": "npm:^0.2.2"
|
||||
"@hookform/resolvers": "npm:^5.0.1"
|
||||
"@prisma/client": "npm:^6.9.0"
|
||||
"@radix-ui/react-dropdown-menu": "npm:^2.1.14"
|
||||
"@radix-ui/react-hover-card": "npm:^1.1.13"
|
||||
|
@ -4066,35 +4038,32 @@ __metadata:
|
|||
"@radix-ui/react-switch": "npm:^1.2.4"
|
||||
"@radix-ui/react-tabs": "npm:^1.1.11"
|
||||
"@tailwindcss/postcss": "npm:4.1.10"
|
||||
"@types/node": "npm:22.15.32"
|
||||
"@types/node": "npm:22.15.31"
|
||||
"@types/react": "npm:19.1.8"
|
||||
"@types/react-big-calendar": "npm:^1"
|
||||
"@types/react-dom": "npm:19.1.6"
|
||||
bcryptjs: "npm:^3.0.2"
|
||||
class-variance-authority: "npm:^0.7.1"
|
||||
clsx: "npm:^2.1.1"
|
||||
date-fns: "npm:^4.1.0"
|
||||
dotenv-cli: "npm:8.0.0"
|
||||
eslint: "npm:9.29.0"
|
||||
eslint: "npm:9.28.0"
|
||||
eslint-config-next: "npm:15.3.3"
|
||||
eslint-config-prettier: "npm:10.1.5"
|
||||
lucide-react: "npm:^0.511.0"
|
||||
next: "npm:15.3.3"
|
||||
next-auth: "npm:^5.0.0-beta.25"
|
||||
next-themes: "npm:^0.4.6"
|
||||
postcss: "npm:8.5.6"
|
||||
postcss: "npm:8.5.5"
|
||||
prettier: "npm:3.5.3"
|
||||
prisma: "npm:6.9.0"
|
||||
react: "npm:^19.1.0"
|
||||
react-big-calendar: "npm:^1.18.0"
|
||||
react-datepicker: "npm:^8.4.0"
|
||||
react-dom: "npm:^19.0.0"
|
||||
react-hook-form: "npm:^7.56.4"
|
||||
tailwind-merge: "npm:^3.2.0"
|
||||
tailwindcss: "npm:4.1.10"
|
||||
tw-animate-css: "npm:1.3.4"
|
||||
typescript: "npm:5.8.3"
|
||||
zod: "npm:^3.25.60"
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
|
@ -4514,14 +4483,14 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss@npm:8.5.6, postcss@npm:^8.4.41":
|
||||
version: 8.5.6
|
||||
resolution: "postcss@npm:8.5.6"
|
||||
"postcss@npm:8.5.5, postcss@npm:^8.4.41":
|
||||
version: 8.5.5
|
||||
resolution: "postcss@npm:8.5.5"
|
||||
dependencies:
|
||||
nanoid: "npm:^3.3.11"
|
||||
picocolors: "npm:^1.1.1"
|
||||
source-map-js: "npm:^1.2.1"
|
||||
checksum: 10c0/5127cc7c91ed7a133a1b7318012d8bfa112da9ef092dddf369ae699a1f10ebbd89b1b9f25f3228795b84585c72aabd5ced5fc11f2ba467eedf7b081a66fad024
|
||||
checksum: 10c0/6415873fab84de05c2d8fd18f72ea6654bca437bb4b9f02ca819c438501e4b3a450023e575e17587c6eaa5bedddaaa4dad3af210f5cf166e30cec09cac58baf8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -4651,15 +4620,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-hook-form@npm:^7.56.4":
|
||||
version: 7.58.0
|
||||
resolution: "react-hook-form@npm:7.58.0"
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17 || ^18 || ^19
|
||||
checksum: 10c0/9e87bf1dfb43157ffec1b112092e8c5b2b9d0056c2a8bdea6c15e08d510b365915101f499f2e7698e16e94541ac82b26ab8e3b05af5b0cccfc82d29cf31c1b0b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-is@npm:^16.13.1":
|
||||
version: 16.13.1
|
||||
resolution: "react-is@npm:16.13.1"
|
||||
|
@ -5683,10 +5643,3 @@ __metadata:
|
|||
checksum: 10c0/dceb44c28578b31641e13695d200d34ec4ab3966a5729814d5445b194933c096b7ced71494ce53a0e8820685d1d010df8b2422e5bf2cdea7e469d97ffbea306f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"zod@npm:^3.25.60":
|
||||
version: 3.25.64
|
||||
resolution: "zod@npm:3.25.64"
|
||||
checksum: 10c0/00d76093a999e377e4ffd037fa7185e861c35917e8c4272f514115c206a0654995168f57fb71708b11e0a9243206d988b7f63b543404e1796402e50d346a6bd7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue