mirror of https://github.com/sylv/micro.git
feat: change password page
This commit is contained in:
parent
eb15ce1ea6
commit
126be8d8c6
|
@ -30,7 +30,7 @@ export class UserResolver {
|
||||||
@InjectRepository(UserVerification) private readonly verificationRepo: EntityRepository<UserVerification>,
|
@InjectRepository(UserVerification) private readonly verificationRepo: EntityRepository<UserVerification>,
|
||||||
private readonly authService: AuthService,
|
private readonly authService: AuthService,
|
||||||
private readonly userService: UserService,
|
private readonly userService: UserService,
|
||||||
private readonly inviteService: InviteService
|
private readonly inviteService: InviteService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@Query(() => User)
|
@Query(() => User)
|
||||||
|
@ -51,7 +51,7 @@ export class UserResolver {
|
||||||
@UserId() userId: string,
|
@UserId() userId: string,
|
||||||
@Parent() user: User,
|
@Parent() user: User,
|
||||||
@Args('first', { nullable: true }) limit: number = 0,
|
@Args('first', { nullable: true }) limit: number = 0,
|
||||||
@Args('after', { nullable: true }) cursor?: string
|
@Args('after', { nullable: true }) cursor?: string,
|
||||||
): Promise<FilePage> {
|
): Promise<FilePage> {
|
||||||
if (userId !== user.id) throw new UnauthorizedException();
|
if (userId !== user.id) throw new UnauthorizedException();
|
||||||
if (limit > 100) limit = 100;
|
if (limit > 100) limit = 100;
|
||||||
|
@ -74,7 +74,7 @@ export class UserResolver {
|
||||||
@UserId() userId: string,
|
@UserId() userId: string,
|
||||||
@Parent() user: User,
|
@Parent() user: User,
|
||||||
@Args('first', { nullable: true }) limit: number = 0,
|
@Args('first', { nullable: true }) limit: number = 0,
|
||||||
@Args('after', { nullable: true }) cursor?: string
|
@Args('after', { nullable: true }) cursor?: string,
|
||||||
): Promise<PastePage> {
|
): Promise<PastePage> {
|
||||||
if (userId !== user.id) throw new UnauthorizedException();
|
if (userId !== user.id) throw new UnauthorizedException();
|
||||||
if (limit > 100) limit = 100;
|
if (limit > 100) limit = 100;
|
||||||
|
@ -119,11 +119,22 @@ export class UserResolver {
|
||||||
return this.userService.createUser(data, invite);
|
return this.userService.createUser(data, invite);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Mutation(() => Boolean)
|
||||||
|
@UseGuards(JWTAuthGuard)
|
||||||
|
async changePassword(
|
||||||
|
@UserId() userId: string,
|
||||||
|
@Args('currentPassword') currentPassword: string,
|
||||||
|
@Args('newPassword') newPassword: string,
|
||||||
|
) {
|
||||||
|
await this.userService.changePassword(userId, currentPassword, newPassword);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Mutation(() => Boolean)
|
@Mutation(() => Boolean)
|
||||||
@UseGuards(JWTAuthGuard)
|
@UseGuards(JWTAuthGuard)
|
||||||
async resendVerificationEmail(
|
async resendVerificationEmail(
|
||||||
@UserId() userId: string,
|
@UserId() userId: string,
|
||||||
@Args('data', { nullable: true }) body?: ResendVerificationEmailDto
|
@Args('data', { nullable: true }) body?: ResendVerificationEmailDto,
|
||||||
) {
|
) {
|
||||||
const user = await this.userService.getUser(userId, false);
|
const user = await this.userService.getUser(userId, false);
|
||||||
const latestVerification = await this.verificationRepo.findOne(
|
const latestVerification = await this.verificationRepo.findOne(
|
||||||
|
@ -137,7 +148,7 @@ export class UserResolver {
|
||||||
orderBy: {
|
orderBy: {
|
||||||
expiresAt: 'DESC',
|
expiresAt: 'DESC',
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (latestVerification && latestVerification.expiresAt.getTime() > Date.now() + UserResolver.MIN_RESEND_INTERVAL) {
|
if (latestVerification && latestVerification.expiresAt.getTime() > Date.now() + UserResolver.MIN_RESEND_INTERVAL) {
|
||||||
|
|
|
@ -125,7 +125,7 @@ export class UserService {
|
||||||
throw new ConflictException('You must provide an email address to create a user.');
|
throw new ConflictException('You must provide an email address to create a user.');
|
||||||
}
|
}
|
||||||
|
|
||||||
const hashedPassword = await bcrypt.hash(data.password, 10);
|
const hashedPassword = await bcrypt.hash(data.password, 12);
|
||||||
const user = this.userRepo.create({
|
const user = this.userRepo.create({
|
||||||
id: generateContentId(),
|
id: generateContentId(),
|
||||||
secret: nanoid(),
|
secret: nanoid(),
|
||||||
|
@ -190,6 +190,17 @@ export class UserService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async changePassword(userId: string, currentPassword: string, newPassword: string) {
|
||||||
|
const user = await this.userRepo.findOneOrFail(userId);
|
||||||
|
const passwordMatches = await bcrypt.compare(currentPassword, user.password);
|
||||||
|
if (!passwordMatches) {
|
||||||
|
throw new BadRequestException('Invalid password');
|
||||||
|
}
|
||||||
|
|
||||||
|
user.password = await bcrypt.hash(newPassword, 12);
|
||||||
|
await this.userRepo.persistAndFlush(user);
|
||||||
|
}
|
||||||
|
|
||||||
@Cron(CronExpression.EVERY_HOUR)
|
@Cron(CronExpression.EVERY_HOUR)
|
||||||
async deleteExpiredVerifications() {
|
async deleteExpiredVerifications() {
|
||||||
await this.verificationRepo.nativeDelete({
|
await this.verificationRepo.nativeDelete({
|
||||||
|
|
|
@ -102,6 +102,7 @@ type Link {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
|
changePassword(currentPassword: String!, newPassword: String!): Boolean!
|
||||||
confirmOTP(otpCode: String!): Boolean!
|
confirmOTP(otpCode: String!): Boolean!
|
||||||
createInvite: Invite!
|
createInvite: Invite!
|
||||||
createLink(destination: String!, host: String): Link!
|
createLink(destination: String!, host: String): Link!
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc --noEmit && rm -rf ./dist/* && vavite build && tsup && rm -rf ./dist/server",
|
"build": "tsc --noEmit && rm -rf ./dist/* && vavite build && tsup && rm -rf ./dist/server",
|
||||||
|
"lint": "eslint --fix ./src/**/*.{ts,tsx}",
|
||||||
"start": "node ./dist/index.js",
|
"start": "node ./dist/index.js",
|
||||||
"watch": "vavite serve"
|
"watch": "vavite serve"
|
||||||
},
|
},
|
||||||
|
@ -57,7 +58,6 @@
|
||||||
"vavite": "^4.0.3",
|
"vavite": "^4.0.3",
|
||||||
"vike": "^0.4.161",
|
"vike": "^0.4.161",
|
||||||
"vite": "^5.1.1",
|
"vite": "^5.1.1",
|
||||||
"vite-plugin-eslint": "^1.8.1",
|
|
||||||
"vite-plugin-graphql-codegen": "^3.3.6",
|
"vite-plugin-graphql-codegen": "^3.3.6",
|
||||||
"yup": "^1.3.3"
|
"yup": "^1.3.3"
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,8 @@ const documents = {
|
||||||
'\n mutation DisableOTP($otpCode: String!) {\n disableOTP(otpCode: $otpCode)\n }\n': types.DisableOtpDocument,
|
'\n mutation DisableOTP($otpCode: String!) {\n disableOTP(otpCode: $otpCode)\n }\n': types.DisableOtpDocument,
|
||||||
'\n query UserQueryWithToken {\n user {\n ...RegularUser\n token\n otpEnabled\n }\n }\n':
|
'\n query UserQueryWithToken {\n user {\n ...RegularUser\n token\n otpEnabled\n }\n }\n':
|
||||||
types.UserQueryWithTokenDocument,
|
types.UserQueryWithTokenDocument,
|
||||||
|
'\n mutation ChangePassword($oldPassword: String!, $newPassword: String!) {\n changePassword(currentPassword: $oldPassword, newPassword: $newPassword)\n }\n':
|
||||||
|
types.ChangePasswordDocument,
|
||||||
'\n query GetFile($fileId: ID!) {\n file(fileId: $fileId) {\n id\n type\n displayName\n size\n sizeFormatted\n textContent\n isOwner\n metadata {\n height\n width\n }\n paths {\n view\n thumbnail\n direct\n }\n urls {\n view\n }\n }\n }\n':
|
'\n query GetFile($fileId: ID!) {\n file(fileId: $fileId) {\n id\n type\n displayName\n size\n sizeFormatted\n textContent\n isOwner\n metadata {\n height\n width\n }\n paths {\n view\n thumbnail\n direct\n }\n urls {\n view\n }\n }\n }\n':
|
||||||
types.GetFileDocument,
|
types.GetFileDocument,
|
||||||
'\n mutation DeleteFile($fileId: ID!, $deleteKey: String) {\n deleteFile(fileId: $fileId, key: $deleteKey)\n }\n':
|
'\n mutation DeleteFile($fileId: ID!, $deleteKey: String) {\n deleteFile(fileId: $fileId, key: $deleteKey)\n }\n':
|
||||||
|
@ -158,6 +160,12 @@ export function graphql(
|
||||||
export function graphql(
|
export function graphql(
|
||||||
source: '\n query UserQueryWithToken {\n user {\n ...RegularUser\n token\n otpEnabled\n }\n }\n',
|
source: '\n query UserQueryWithToken {\n user {\n ...RegularUser\n token\n otpEnabled\n }\n }\n',
|
||||||
): (typeof documents)['\n query UserQueryWithToken {\n user {\n ...RegularUser\n token\n otpEnabled\n }\n }\n'];
|
): (typeof documents)['\n query UserQueryWithToken {\n user {\n ...RegularUser\n token\n otpEnabled\n }\n }\n'];
|
||||||
|
/**
|
||||||
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
|
*/
|
||||||
|
export function graphql(
|
||||||
|
source: '\n mutation ChangePassword($oldPassword: String!, $newPassword: String!) {\n changePassword(currentPassword: $oldPassword, newPassword: $newPassword)\n }\n',
|
||||||
|
): (typeof documents)['\n mutation ChangePassword($oldPassword: String!, $newPassword: String!) {\n changePassword(currentPassword: $oldPassword, newPassword: $newPassword)\n }\n'];
|
||||||
/**
|
/**
|
||||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -118,6 +118,7 @@ export type Link = {
|
||||||
|
|
||||||
export type Mutation = {
|
export type Mutation = {
|
||||||
__typename?: 'Mutation';
|
__typename?: 'Mutation';
|
||||||
|
changePassword: Scalars['Boolean']['output'];
|
||||||
confirmOTP: Scalars['Boolean']['output'];
|
confirmOTP: Scalars['Boolean']['output'];
|
||||||
createInvite: Invite;
|
createInvite: Invite;
|
||||||
createLink: Link;
|
createLink: Link;
|
||||||
|
@ -131,6 +132,11 @@ export type Mutation = {
|
||||||
resendVerificationEmail: Scalars['Boolean']['output'];
|
resendVerificationEmail: Scalars['Boolean']['output'];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type MutationChangePasswordArgs = {
|
||||||
|
currentPassword: Scalars['String']['input'];
|
||||||
|
newPassword: Scalars['String']['input'];
|
||||||
|
};
|
||||||
|
|
||||||
export type MutationConfirmOtpArgs = {
|
export type MutationConfirmOtpArgs = {
|
||||||
otpCode: Scalars['String']['input'];
|
otpCode: Scalars['String']['input'];
|
||||||
};
|
};
|
||||||
|
@ -458,6 +464,13 @@ export type UserQueryWithTokenQuery = {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ChangePasswordMutationVariables = Exact<{
|
||||||
|
oldPassword: Scalars['String']['input'];
|
||||||
|
newPassword: Scalars['String']['input'];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export type ChangePasswordMutation = { __typename?: 'Mutation'; changePassword: boolean };
|
||||||
|
|
||||||
export type GetFileQueryVariables = Exact<{
|
export type GetFileQueryVariables = Exact<{
|
||||||
fileId: Scalars['ID']['input'];
|
fileId: Scalars['ID']['input'];
|
||||||
}>;
|
}>;
|
||||||
|
@ -1250,6 +1263,49 @@ export const UserQueryWithTokenDocument = {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
} as unknown as DocumentNode<UserQueryWithTokenQuery, UserQueryWithTokenQueryVariables>;
|
} as unknown as DocumentNode<UserQueryWithTokenQuery, UserQueryWithTokenQueryVariables>;
|
||||||
|
export const ChangePasswordDocument = {
|
||||||
|
kind: 'Document',
|
||||||
|
definitions: [
|
||||||
|
{
|
||||||
|
kind: 'OperationDefinition',
|
||||||
|
operation: 'mutation',
|
||||||
|
name: { kind: 'Name', value: 'ChangePassword' },
|
||||||
|
variableDefinitions: [
|
||||||
|
{
|
||||||
|
kind: 'VariableDefinition',
|
||||||
|
variable: { kind: 'Variable', name: { kind: 'Name', value: 'oldPassword' } },
|
||||||
|
type: { kind: 'NonNullType', type: { kind: 'NamedType', name: { kind: 'Name', value: 'String' } } },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: 'VariableDefinition',
|
||||||
|
variable: { kind: 'Variable', name: { kind: 'Name', value: 'newPassword' } },
|
||||||
|
type: { kind: 'NonNullType', type: { kind: 'NamedType', name: { kind: 'Name', value: 'String' } } },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
selectionSet: {
|
||||||
|
kind: 'SelectionSet',
|
||||||
|
selections: [
|
||||||
|
{
|
||||||
|
kind: 'Field',
|
||||||
|
name: { kind: 'Name', value: 'changePassword' },
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
kind: 'Argument',
|
||||||
|
name: { kind: 'Name', value: 'currentPassword' },
|
||||||
|
value: { kind: 'Variable', name: { kind: 'Name', value: 'oldPassword' } },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: 'Argument',
|
||||||
|
name: { kind: 'Name', value: 'newPassword' },
|
||||||
|
value: { kind: 'Variable', name: { kind: 'Name', value: 'newPassword' } },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
} as unknown as DocumentNode<ChangePasswordMutation, ChangePasswordMutationVariables>;
|
||||||
export const GetFileDocument = {
|
export const GetFileDocument = {
|
||||||
kind: 'Document',
|
kind: 'Document',
|
||||||
definitions: [
|
definitions: [
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
import { useContext } from "react";
|
import { useContext } from 'react';
|
||||||
import { ToastContext } from "./context";
|
import { ToastContext } from './context';
|
||||||
|
|
||||||
export const useToasts = () => {
|
export const useToasts = () => {
|
||||||
const createToast = useContext(ToastContext);
|
const createToast = useContext(ToastContext);
|
||||||
if (!createToast) {
|
if (!createToast) {
|
||||||
// todo: this should be an error, but it seems like it can be undefined.
|
if (typeof window === 'undefined') return () => {};
|
||||||
// maybe due to concurrent rendering? idk shit about fuck.
|
throw new Error('useToasts must be used within a ToastProvider');
|
||||||
return () => undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return createToast;
|
return createToast;
|
||||||
|
|
|
@ -39,11 +39,9 @@ export const LoginForm: FC = () => {
|
||||||
setLoginInfo(values);
|
setLoginInfo(values);
|
||||||
setInvalidOTP(false);
|
setInvalidOTP(false);
|
||||||
await user.login(values);
|
await user.login(values);
|
||||||
console.log('ball');
|
|
||||||
setError(null);
|
setError(null);
|
||||||
redirect();
|
redirect();
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.log(error);
|
|
||||||
if (user.otpRequired && error.message.toLowerCase().includes('invalid otp')) {
|
if (user.otpRequired && error.message.toLowerCase().includes('invalid otp')) {
|
||||||
setInvalidOTP(true);
|
setInvalidOTP(true);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
import { Form, Formik } from 'formik';
|
||||||
|
import type { FC } from 'react';
|
||||||
|
import { Fragment } from 'react';
|
||||||
|
import * as Yup from 'yup';
|
||||||
|
import { Input } from '../components/input/input';
|
||||||
|
import { Submit } from '../components/input/submit';
|
||||||
|
|
||||||
|
interface PasswordData {
|
||||||
|
oldPassword: string;
|
||||||
|
newPassword: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PasswordFormProps {
|
||||||
|
onSubmit: (data: PasswordData) => Promise<void> | void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const schema = Yup.object().shape({
|
||||||
|
oldPassword: Yup.string().required().min(5),
|
||||||
|
newPassword: Yup.string().required().min(5),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const PasswordForm: FC<PasswordFormProps> = ({ onSubmit }) => {
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<Formik
|
||||||
|
validationSchema={schema}
|
||||||
|
initialValues={{
|
||||||
|
oldPassword: '',
|
||||||
|
newPassword: '',
|
||||||
|
}}
|
||||||
|
onSubmit={async (values) => {
|
||||||
|
await onSubmit(values);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Form className="space-y-2">
|
||||||
|
<Input id="oldPassword" type="password" placeholder="Current Password" autoComplete="current-password" />
|
||||||
|
<Input id="newPassword" type="password" placeholder="New Password" autoComplete="new-password" />
|
||||||
|
<Submit className="mt-4 w-full" type="submit">
|
||||||
|
Change Password
|
||||||
|
</Submit>
|
||||||
|
</Form>
|
||||||
|
</Formik>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
};
|
|
@ -1,10 +1,10 @@
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
import type { CombinedError, TypedDocumentNode } from '@urql/preact';
|
import type { CombinedError, TypedDocumentNode } from '@urql/preact';
|
||||||
import { useMutation, useQuery } from '@urql/preact';
|
import { useMutation, useQuery } from '@urql/preact';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
import { graphql } from '../@generated/gql';
|
import { graphql } from '../@generated/gql';
|
||||||
import type { GetUserQuery, LoginMutationVariables } from '../@generated/graphql';
|
import type { GetUserQuery, LoginMutationVariables } from '../@generated/graphql';
|
||||||
import { navigate, reload } from '../helpers/routing';
|
|
||||||
import { type RegularUserFragment } from '../@generated/graphql';
|
import { type RegularUserFragment } from '../@generated/graphql';
|
||||||
|
import { navigate } from '../helpers/routing';
|
||||||
import { useAsync } from './useAsync';
|
import { useAsync } from './useAsync';
|
||||||
|
|
||||||
const RegularUserFragment = graphql(`
|
const RegularUserFragment = graphql(`
|
||||||
|
@ -42,15 +42,16 @@ export const useLoginUser = () => {
|
||||||
const [otp, setOtp] = useState(false);
|
const [otp, setOtp] = useState(false);
|
||||||
const [, loginMutation] = useMutation(LoginMutation);
|
const [, loginMutation] = useMutation(LoginMutation);
|
||||||
const [login] = useAsync(async (variables: LoginMutationVariables) => {
|
const [login] = useAsync(async (variables: LoginMutationVariables) => {
|
||||||
try {
|
const result = await loginMutation(variables);
|
||||||
await loginMutation(variables);
|
if (result.data) {
|
||||||
navigate('/dashboard');
|
navigate('/dashboard');
|
||||||
} catch (error: any) {
|
} else if (result.error) {
|
||||||
if (error.message.toLowerCase().includes('otp')) {
|
if (result.error.message.toLowerCase().includes('otp')) {
|
||||||
setOtp(true);
|
setOtp(true);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw error;
|
throw result.error;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -64,7 +65,7 @@ export const useLogoutUser = () => {
|
||||||
const [, logoutMutation] = useMutation(LogoutMutation);
|
const [, logoutMutation] = useMutation(LogoutMutation);
|
||||||
const [logout] = useAsync(async () => {
|
const [logout] = useAsync(async () => {
|
||||||
await logoutMutation({});
|
await logoutMutation({});
|
||||||
reload();
|
navigate('/');
|
||||||
});
|
});
|
||||||
|
|
||||||
return { logout };
|
return { logout };
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
import { useMutation, useQuery } from '@urql/preact';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
import { Fragment } from 'react';
|
import { Fragment } from 'react';
|
||||||
import { useMutation, useQuery } from '@urql/preact';
|
|
||||||
import { graphql } from '../../../@generated/gql';
|
import { graphql } from '../../../@generated/gql';
|
||||||
import { Breadcrumbs } from '../../../components/breadcrumbs';
|
import { Breadcrumbs } from '../../../components/breadcrumbs';
|
||||||
import { Button } from '../../../components/button';
|
import { Button } from '../../../components/button';
|
||||||
|
@ -98,6 +98,32 @@ export const Page: FC = () => {
|
||||||
{!user.data && <InputSkeleton />}
|
{!user.data && <InputSkeleton />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="grid grid-cols-2 gap-4 mt-8">
|
||||||
|
<div className="left col-span-full md:col-span-1">
|
||||||
|
{user.data && (
|
||||||
|
<Fragment>
|
||||||
|
<div className="font-bold text-xl">Change Password</div>
|
||||||
|
<p className="text-sm mt-2 text-gray-400">
|
||||||
|
Change your account password. This will not sign out other devices.
|
||||||
|
</p>
|
||||||
|
</Fragment>
|
||||||
|
)}
|
||||||
|
{!user.data && (
|
||||||
|
<Fragment>
|
||||||
|
<Skeleton className="w-1/2 mb-1" />
|
||||||
|
<Skeleton className="w-3/4" />
|
||||||
|
</Fragment>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="right flex items-center col-span-full md:col-span-1">
|
||||||
|
{user.data && (
|
||||||
|
<Button className="w-auto ml-auto" onClick={() => navigate(`/dashboard/preferences/change-password`)}>
|
||||||
|
Change
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{!user.data && <ButtonSkeleton className="ml-auto" />}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div className="mt-10">
|
<div className="mt-10">
|
||||||
<ConfigGenerator user={user.data?.user} />
|
<ConfigGenerator user={user.data?.user} />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
import { useMutation } from '@urql/preact';
|
||||||
|
import type { FC } from 'react';
|
||||||
|
import { graphql } from '../../../../@generated';
|
||||||
|
import type { ChangePasswordMutationVariables } from '../../../../@generated/graphql';
|
||||||
|
import { Breadcrumbs } from '../../../../components/breadcrumbs';
|
||||||
|
import { Container } from '../../../../components/container';
|
||||||
|
import { Title } from '../../../../components/title';
|
||||||
|
import { useToasts } from '../../../../components/toast';
|
||||||
|
import { PasswordForm } from '../../../../containers/password-form';
|
||||||
|
import { navigate, prefetch } from '../../../../helpers/routing';
|
||||||
|
import { useAsync } from '../../../../hooks/useAsync';
|
||||||
|
import { useUser } from '../../../../hooks/useUser';
|
||||||
|
|
||||||
|
const ChangePassword = graphql(`
|
||||||
|
mutation ChangePassword($oldPassword: String!, $newPassword: String!) {
|
||||||
|
changePassword(currentPassword: $oldPassword, newPassword: $newPassword)
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
export const Page: FC = () => {
|
||||||
|
const createToast = useToasts();
|
||||||
|
const [, changeInner] = useMutation(ChangePassword);
|
||||||
|
const [change] = useAsync(async (values: ChangePasswordMutationVariables) => {
|
||||||
|
prefetch('/dashboard/preferences');
|
||||||
|
const result = await changeInner(values);
|
||||||
|
if (result.data) {
|
||||||
|
createToast({ text: 'Your password has been changed.' });
|
||||||
|
navigate('/dashboard/preferences');
|
||||||
|
} else if (result.error) {
|
||||||
|
if (result.error.message.toLowerCase().includes('unauthorized')) {
|
||||||
|
createToast({ text: 'Invalid password.' });
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
createToast({ text: 'An error occurred changing your password.' });
|
||||||
|
}
|
||||||
|
|
||||||
|
throw result.error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
useUser();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<Title>Change Password</Title>
|
||||||
|
<div className="flex justify-center top-[40vh]">
|
||||||
|
<div className="w-[80vw] md:w-[50vw] lg:w-[30vw]">
|
||||||
|
<Breadcrumbs href="/dashboard/preferences" className="mb-4">
|
||||||
|
Dashboard / Preferences
|
||||||
|
</Breadcrumbs>
|
||||||
|
<h1 className="my-5 text-4xl font-bold">Change Password</h1>
|
||||||
|
<PasswordForm onSubmit={change} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
|
@ -2,7 +2,6 @@ import { preact } from '@preact/preset-vite';
|
||||||
import { vavite } from 'vavite';
|
import { vavite } from 'vavite';
|
||||||
import ssr from 'vike/plugin';
|
import ssr from 'vike/plugin';
|
||||||
import { defineConfig } from 'vite';
|
import { defineConfig } from 'vite';
|
||||||
import eslint from 'vite-plugin-eslint';
|
|
||||||
import codegen from 'vite-plugin-graphql-codegen';
|
import codegen from 'vite-plugin-graphql-codegen';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
@ -25,7 +24,6 @@ export default defineConfig({
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
codegen(),
|
codegen(),
|
||||||
eslint({ cache: true }),
|
|
||||||
preact(),
|
preact(),
|
||||||
ssr({ disableAutoFullBuild: true }),
|
ssr({ disableAutoFullBuild: true }),
|
||||||
vavite({
|
vavite({
|
||||||
|
|
116
pnpm-lock.yaml
116
pnpm-lock.yaml
|
@ -353,10 +353,7 @@ importers:
|
||||||
version: 0.4.161(vite@5.1.1)
|
version: 0.4.161(vite@5.1.1)
|
||||||
vite:
|
vite:
|
||||||
specifier: ^5.1.1
|
specifier: ^5.1.1
|
||||||
version: 5.1.1(@types/node@20.11.17)
|
version: 5.1.1
|
||||||
vite-plugin-eslint:
|
|
||||||
specifier: ^1.8.1
|
|
||||||
version: 1.8.1(eslint@8.56.0)(vite@5.1.1)
|
|
||||||
vite-plugin-graphql-codegen:
|
vite-plugin-graphql-codegen:
|
||||||
specifier: ^3.3.6
|
specifier: ^3.3.6
|
||||||
version: 3.3.6(@graphql-codegen/cli@5.0.2)(graphql@16.8.1)(vite@5.1.1)
|
version: 3.3.6(@graphql-codegen/cli@5.0.2)(graphql@16.8.1)(vite@5.1.1)
|
||||||
|
@ -1389,7 +1386,7 @@ packages:
|
||||||
'@fastify/reply-from': 9.7.0
|
'@fastify/reply-from': 9.7.0
|
||||||
fast-querystring: 1.1.2
|
fast-querystring: 1.1.2
|
||||||
fastify-plugin: 4.5.1
|
fastify-plugin: 4.5.1
|
||||||
ws: 8.16.0(utf-8-validate@6.0.3)
|
ws: 8.16.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- bufferutil
|
- bufferutil
|
||||||
- utf-8-validate
|
- utf-8-validate
|
||||||
|
@ -1783,7 +1780,7 @@ packages:
|
||||||
graphql-ws: 5.14.3(graphql@16.8.1)
|
graphql-ws: 5.14.3(graphql@16.8.1)
|
||||||
isomorphic-ws: 5.0.0(ws@8.16.0)
|
isomorphic-ws: 5.0.0(ws@8.16.0)
|
||||||
tslib: 2.6.2
|
tslib: 2.6.2
|
||||||
ws: 8.16.0(utf-8-validate@6.0.3)
|
ws: 8.16.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- bufferutil
|
- bufferutil
|
||||||
- utf-8-validate
|
- utf-8-validate
|
||||||
|
@ -1818,7 +1815,7 @@ packages:
|
||||||
graphql: 16.8.1
|
graphql: 16.8.1
|
||||||
isomorphic-ws: 5.0.0(ws@8.16.0)
|
isomorphic-ws: 5.0.0(ws@8.16.0)
|
||||||
tslib: 2.6.2
|
tslib: 2.6.2
|
||||||
ws: 8.16.0(utf-8-validate@6.0.3)
|
ws: 8.16.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- bufferutil
|
- bufferutil
|
||||||
- utf-8-validate
|
- utf-8-validate
|
||||||
|
@ -2044,7 +2041,7 @@ packages:
|
||||||
isomorphic-ws: 5.0.0(ws@8.16.0)
|
isomorphic-ws: 5.0.0(ws@8.16.0)
|
||||||
tslib: 2.6.2
|
tslib: 2.6.2
|
||||||
value-or-promise: 1.0.12
|
value-or-promise: 1.0.12
|
||||||
ws: 8.16.0(utf-8-validate@6.0.3)
|
ws: 8.16.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@types/node'
|
- '@types/node'
|
||||||
- bufferutil
|
- bufferutil
|
||||||
|
@ -3099,7 +3096,7 @@ packages:
|
||||||
magic-string: 0.30.5
|
magic-string: 0.30.5
|
||||||
node-html-parser: 6.1.12
|
node-html-parser: 6.1.12
|
||||||
resolve: 1.22.8
|
resolve: 1.22.8
|
||||||
vite: 5.1.1(@types/node@20.11.17)
|
vite: 5.1.1
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- preact
|
- preact
|
||||||
- supports-color
|
- supports-color
|
||||||
|
@ -3133,7 +3130,7 @@ packages:
|
||||||
'@prefresh/utils': 1.2.0
|
'@prefresh/utils': 1.2.0
|
||||||
'@rollup/pluginutils': 4.2.1
|
'@rollup/pluginutils': 4.2.1
|
||||||
preact: 10.19.4
|
preact: 10.19.4
|
||||||
vite: 5.1.1(@types/node@20.11.17)
|
vite: 5.1.1
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -3848,13 +3845,6 @@ packages:
|
||||||
resolution: {integrity: sha512-kRiitIeUg1mPV9yH4VUJ/1uk2XjyANfeL8/7rH1tsjvHeO9PJLBHJIYsFWmAvmGj5u8rj+1TZx7PZzW2qLw3Lw==}
|
resolution: {integrity: sha512-kRiitIeUg1mPV9yH4VUJ/1uk2XjyANfeL8/7rH1tsjvHeO9PJLBHJIYsFWmAvmGj5u8rj+1TZx7PZzW2qLw3Lw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@types/eslint@8.56.2:
|
|
||||||
resolution: {integrity: sha512-uQDwm1wFHmbBbCZCqAlq6Do9LYwByNZHWzXppSnay9SuwJ+VRbjkbLABer54kcPnMSlG6Fdiy2yaFXm/z9Z5gw==}
|
|
||||||
dependencies:
|
|
||||||
'@types/estree': 1.0.5
|
|
||||||
'@types/json-schema': 7.0.15
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@types/estree-jsx@1.0.3:
|
/@types/estree-jsx@1.0.3:
|
||||||
resolution: {integrity: sha512-pvQ+TKeRHeiUGRhvYwRrQ/ISnohKkSJR14fT2yqyZ4e9K5vqc7hrtY2Y1Dw0ZwAzQ6DQsxsaCUuSIIi8v0Cq6w==}
|
resolution: {integrity: sha512-pvQ+TKeRHeiUGRhvYwRrQ/ISnohKkSJR14fT2yqyZ4e9K5vqc7hrtY2Y1Dw0ZwAzQ6DQsxsaCUuSIIi8v0Cq6w==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -4251,7 +4241,7 @@ packages:
|
||||||
vite: ^2.8.1 || 3 || 4 || 5
|
vite: ^2.8.1 || 3 || 4 || 5
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 20.11.17
|
'@types/node': 20.11.17
|
||||||
vite: 5.1.1(@types/node@20.11.17)
|
vite: 5.1.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@vavite/expose-vite-dev-server@4.0.3(vite@5.1.1):
|
/@vavite/expose-vite-dev-server@4.0.3(vite@5.1.1):
|
||||||
|
@ -4259,7 +4249,7 @@ packages:
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
vite: ^2.8.1 || 3 || 4 || 5
|
vite: ^2.8.1 || 3 || 4 || 5
|
||||||
dependencies:
|
dependencies:
|
||||||
vite: 5.1.1(@types/node@20.11.17)
|
vite: 5.1.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@vavite/multibuild-cli@4.0.3(vite@5.1.1):
|
/@vavite/multibuild-cli@4.0.3(vite@5.1.1):
|
||||||
|
@ -4272,7 +4262,7 @@ packages:
|
||||||
'@vavite/multibuild': 4.0.3(vite@5.1.1)
|
'@vavite/multibuild': 4.0.3(vite@5.1.1)
|
||||||
cac: 6.7.14
|
cac: 6.7.14
|
||||||
picocolors: 1.0.0
|
picocolors: 1.0.0
|
||||||
vite: 5.1.1(@types/node@20.11.17)
|
vite: 5.1.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@vavite/multibuild@4.0.3(vite@5.1.1):
|
/@vavite/multibuild@4.0.3(vite@5.1.1):
|
||||||
|
@ -4283,7 +4273,7 @@ packages:
|
||||||
'@types/node': 20.11.17
|
'@types/node': 20.11.17
|
||||||
cac: 6.7.14
|
cac: 6.7.14
|
||||||
picocolors: 1.0.0
|
picocolors: 1.0.0
|
||||||
vite: 5.1.1(@types/node@20.11.17)
|
vite: 5.1.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@vavite/node-loader@4.0.3(vite@5.1.1):
|
/@vavite/node-loader@4.0.3(vite@5.1.1):
|
||||||
|
@ -4292,7 +4282,7 @@ packages:
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
vite: ^2.8.1 || 3 || 4 || 5
|
vite: ^2.8.1 || 3 || 4 || 5
|
||||||
dependencies:
|
dependencies:
|
||||||
vite: 5.1.1(@types/node@20.11.17)
|
vite: 5.1.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@vavite/reloader@4.0.3(vite@5.1.1):
|
/@vavite/reloader@4.0.3(vite@5.1.1):
|
||||||
|
@ -4300,7 +4290,7 @@ packages:
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
vite: ^2.8.1 || 3 || 4 || 5
|
vite: ^2.8.1 || 3 || 4 || 5
|
||||||
dependencies:
|
dependencies:
|
||||||
vite: 5.1.1(@types/node@20.11.17)
|
vite: 5.1.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@vitest/expect@1.2.2:
|
/@vitest/expect@1.2.2:
|
||||||
|
@ -7214,7 +7204,7 @@ packages:
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
ws: '*'
|
ws: '*'
|
||||||
dependencies:
|
dependencies:
|
||||||
ws: 8.16.0(utf-8-validate@6.0.3)
|
ws: 8.16.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/istextorbinary@9.5.0:
|
/istextorbinary@9.5.0:
|
||||||
|
@ -8356,6 +8346,7 @@ packages:
|
||||||
/node-gyp-build@4.8.0:
|
/node-gyp-build@4.8.0:
|
||||||
resolution: {integrity: sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==}
|
resolution: {integrity: sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
dev: false
|
||||||
|
|
||||||
/node-html-parser@6.1.12:
|
/node-html-parser@6.1.12:
|
||||||
resolution: {integrity: sha512-/bT/Ncmv+fbMGX96XG9g05vFt43m/+SYKIs9oAemQVYyVcZmDAI2Xq/SbNcpOA35eF0Zk2av3Ksf+Xk8Vt8abA==}
|
resolution: {integrity: sha512-/bT/Ncmv+fbMGX96XG9g05vFt43m/+SYKIs9oAemQVYyVcZmDAI2Xq/SbNcpOA35eF0Zk2av3Ksf+Xk8Vt8abA==}
|
||||||
|
@ -9509,14 +9500,6 @@ packages:
|
||||||
glob: 7.2.3
|
glob: 7.2.3
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/rollup@2.79.1:
|
|
||||||
resolution: {integrity: sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==}
|
|
||||||
engines: {node: '>=10.0.0'}
|
|
||||||
hasBin: true
|
|
||||||
optionalDependencies:
|
|
||||||
fsevents: 2.3.3
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/rollup@4.10.0:
|
/rollup@4.10.0:
|
||||||
resolution: {integrity: sha512-t2v9G2AKxcQ8yrG+WGxctBes1AomT0M4ND7jTFBCVPXQ/WFTvNSefIrNSmLKhIKBrvN8SG+CZslimJcT3W2u2g==}
|
resolution: {integrity: sha512-t2v9G2AKxcQ8yrG+WGxctBes1AomT0M4ND7jTFBCVPXQ/WFTvNSefIrNSmLKhIKBrvN8SG+CZslimJcT3W2u2g==}
|
||||||
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
||||||
|
@ -10808,6 +10791,7 @@ packages:
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dependencies:
|
dependencies:
|
||||||
node-gyp-build: 4.8.0
|
node-gyp-build: 4.8.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/util-deprecate@1.0.2:
|
/util-deprecate@1.0.2:
|
||||||
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
||||||
|
@ -10864,7 +10848,7 @@ packages:
|
||||||
'@vavite/reloader': 4.0.3(vite@5.1.1)
|
'@vavite/reloader': 4.0.3(vite@5.1.1)
|
||||||
cac: 6.7.14
|
cac: 6.7.14
|
||||||
picocolors: 1.0.0
|
picocolors: 1.0.0
|
||||||
vite: 5.1.1(@types/node@20.11.17)
|
vite: 5.1.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/version-range@4.14.0:
|
/version-range@4.14.0:
|
||||||
|
@ -10910,7 +10894,7 @@ packages:
|
||||||
fast-glob: 3.3.2
|
fast-glob: 3.3.2
|
||||||
sirv: 2.0.4
|
sirv: 2.0.4
|
||||||
source-map-support: 0.5.21
|
source-map-support: 0.5.21
|
||||||
vite: 5.1.1(@types/node@20.11.17)
|
vite: 5.1.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/vite-node@1.2.2(@types/node@20.11.17):
|
/vite-node@1.2.2(@types/node@20.11.17):
|
||||||
|
@ -10934,19 +10918,6 @@ packages:
|
||||||
- terser
|
- terser
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/vite-plugin-eslint@1.8.1(eslint@8.56.0)(vite@5.1.1):
|
|
||||||
resolution: {integrity: sha512-PqdMf3Y2fLO9FsNPmMX+//2BF5SF8nEWspZdgl4kSt7UvHDRHVVfHvxsD7ULYzZrJDGRxR81Nq7TOFgwMnUang==}
|
|
||||||
peerDependencies:
|
|
||||||
eslint: '>=7'
|
|
||||||
vite: '>=2'
|
|
||||||
dependencies:
|
|
||||||
'@rollup/pluginutils': 4.2.1
|
|
||||||
'@types/eslint': 8.56.2
|
|
||||||
eslint: 8.56.0
|
|
||||||
rollup: 2.79.1
|
|
||||||
vite: 5.1.1(@types/node@20.11.17)
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/vite-plugin-graphql-codegen@3.3.6(@graphql-codegen/cli@5.0.2)(graphql@16.8.1)(vite@5.1.1):
|
/vite-plugin-graphql-codegen@3.3.6(@graphql-codegen/cli@5.0.2)(graphql@16.8.1)(vite@5.1.1):
|
||||||
resolution: {integrity: sha512-TXMaUpPCfqzSpujjzFjVeeCH9JOSBwFWxOJottZ+gouQtNhnNpgXcj4nZep3om5Wq0UlDwDYLqXWrAa8XaZW1w==}
|
resolution: {integrity: sha512-TXMaUpPCfqzSpujjzFjVeeCH9JOSBwFWxOJottZ+gouQtNhnNpgXcj4nZep3om5Wq0UlDwDYLqXWrAa8XaZW1w==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
@ -10957,7 +10928,42 @@ packages:
|
||||||
'@graphql-codegen/cli': 5.0.2(@parcel/watcher@2.4.0)(graphql@16.8.1)(typescript@5.3.3)
|
'@graphql-codegen/cli': 5.0.2(@parcel/watcher@2.4.0)(graphql@16.8.1)(typescript@5.3.3)
|
||||||
'@graphql-codegen/plugin-helpers': 5.0.3(graphql@16.8.1)
|
'@graphql-codegen/plugin-helpers': 5.0.3(graphql@16.8.1)
|
||||||
graphql: 16.8.1
|
graphql: 16.8.1
|
||||||
vite: 5.1.1(@types/node@20.11.17)
|
vite: 5.1.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/vite@5.1.1:
|
||||||
|
resolution: {integrity: sha512-wclpAgY3F1tR7t9LL5CcHC41YPkQIpKUGeIuT8MdNwNZr6OqOTLs7JX5vIHAtzqLWXts0T+GDrh9pN2arneKqg==}
|
||||||
|
engines: {node: ^18.0.0 || >=20.0.0}
|
||||||
|
hasBin: true
|
||||||
|
peerDependencies:
|
||||||
|
'@types/node': ^18.0.0 || >=20.0.0
|
||||||
|
less: '*'
|
||||||
|
lightningcss: ^1.21.0
|
||||||
|
sass: '*'
|
||||||
|
stylus: '*'
|
||||||
|
sugarss: '*'
|
||||||
|
terser: ^5.4.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
less:
|
||||||
|
optional: true
|
||||||
|
lightningcss:
|
||||||
|
optional: true
|
||||||
|
sass:
|
||||||
|
optional: true
|
||||||
|
stylus:
|
||||||
|
optional: true
|
||||||
|
sugarss:
|
||||||
|
optional: true
|
||||||
|
terser:
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
esbuild: 0.19.12
|
||||||
|
postcss: 8.4.35
|
||||||
|
rollup: 4.10.0
|
||||||
|
optionalDependencies:
|
||||||
|
fsevents: 2.3.3
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/vite@5.1.1(@types/node@20.11.17):
|
/vite@5.1.1(@types/node@20.11.17):
|
||||||
|
@ -11224,6 +11230,19 @@ packages:
|
||||||
utf-8-validate: 6.0.3
|
utf-8-validate: 6.0.3
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/ws@8.16.0:
|
||||||
|
resolution: {integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==}
|
||||||
|
engines: {node: '>=10.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
bufferutil: ^4.0.1
|
||||||
|
utf-8-validate: '>=5.0.2'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
bufferutil:
|
||||||
|
optional: true
|
||||||
|
utf-8-validate:
|
||||||
|
optional: true
|
||||||
|
dev: true
|
||||||
|
|
||||||
/ws@8.16.0(utf-8-validate@6.0.3):
|
/ws@8.16.0(utf-8-validate@6.0.3):
|
||||||
resolution: {integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==}
|
resolution: {integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==}
|
||||||
engines: {node: '>=10.0.0'}
|
engines: {node: '>=10.0.0'}
|
||||||
|
@ -11237,6 +11256,7 @@ packages:
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
utf-8-validate: 6.0.3
|
utf-8-validate: 6.0.3
|
||||||
|
dev: false
|
||||||
|
|
||||||
/xtend@4.0.2:
|
/xtend@4.0.2:
|
||||||
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
|
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
|
||||||
|
|
Loading…
Reference in New Issue