From 3990c1a4adb8d37ca5076e3058fa8994a87a0d8e Mon Sep 17 00:00:00 2001 From: Meierschlumpf Date: Sun, 23 Jul 2023 14:18:10 +0200 Subject: [PATCH 001/233] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Add=20env=20variab?= =?UTF-8?q?le=20validation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 23 +++++++ next.config.js | 1 + package.json | 5 +- .../Customization/CustomizationAccordeon.tsx | 3 +- src/env.js | 66 +++++++++++++++++++ src/pages/_app.tsx | 17 ++--- src/pages/api/docker/DockerSingleton.ts | 7 +- src/pages/api/trpc/[trpc].ts | 3 +- .../api/routers/docker/DockerSingleton.ts | 7 +- src/tools/server/getPackageVersion.ts | 4 +- src/utils/api.ts | 6 +- yarn.lock | 23 +++++++ 12 files changed, 140 insertions(+), 25 deletions(-) create mode 100644 .env.example create mode 100644 src/env.js diff --git a/.env.example b/.env.example new file mode 100644 index 000000000..37c6640d7 --- /dev/null +++ b/.env.example @@ -0,0 +1,23 @@ +# Since the ".env" file is gitignored, you can use the ".env.example" file to +# build a new ".env" file when you clone the repo. Keep this file up-to-date +# when you add new variables to `.env`. + +# This file will be committed to version control, so make sure not to have any +# secrets in it. If you are cloning this repo, create a copy of this file named +# ".env" and populate it with your secrets. + +# When adding additional environment variables, the schema in "/src/env.js" +# should be updated accordingly. + +# Prisma +# https://www.prisma.io/docs/reference/database-reference/connection-urls#env +DATABASE_URL="file:./db.sqlite" + +# Next Auth +# You can generate a new secret on the command line with: +# openssl rand -base64 32 +# https://next-auth.js.org/configuration/options#secret +# NEXTAUTH_SECRET="" +NEXTAUTH_URL="http://localhost:3000" + +NEXTAUTH_SECRET="" diff --git a/next.config.js b/next.config.js index cd8c563ea..2e183dbb4 100644 --- a/next.config.js +++ b/next.config.js @@ -1,3 +1,4 @@ +require('./src/env'); const { i18n } = require('./next-i18next.config'); const withBundleAnalyzer = require('@next/bundle-analyzer')({ diff --git a/package.json b/package.json index 49f0b7b82..b2ac01ced 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "@nivo/core": "^0.83.0", "@nivo/line": "^0.83.0", "@react-native-async-storage/async-storage": "^1.18.1", + "@t3-oss/env-nextjs": "^0.6.0", "@tabler/icons-react": "^2.18.0", "@tanstack/query-async-storage-persister": "^4.27.1", "@tanstack/query-sync-storage-persister": "^4.27.1", @@ -155,7 +156,9 @@ "^[./]" ], "importOrderSeparation": true, - "plugins": ["@trivago/prettier-plugin-sort-imports"], + "plugins": [ + "@trivago/prettier-plugin-sort-imports" + ], "importOrderSortSpecifiers": true }, "eslintConfig": { diff --git a/src/components/Settings/Customization/CustomizationAccordeon.tsx b/src/components/Settings/Customization/CustomizationAccordeon.tsx index 939b8c2ac..33789f50c 100644 --- a/src/components/Settings/Customization/CustomizationAccordeon.tsx +++ b/src/components/Settings/Customization/CustomizationAccordeon.tsx @@ -9,6 +9,7 @@ import { } from '@tabler/icons-react'; import { i18n, useTranslation } from 'next-i18next'; import { ReactNode } from 'react'; +import { env } from '~/env'; import { AccessibilitySettings } from './Accessibility/AccessibilitySettings'; import { GridstackConfiguration } from './Layout/GridstackConfiguration'; @@ -130,7 +131,7 @@ const getItems = () => { ), }, ]; - if (process.env.NODE_ENV === 'development') { + if (env.NEXT_PUBLIC_NODE_ENV === 'development') { items.push({ id: 'dev', image: , diff --git a/src/env.js b/src/env.js new file mode 100644 index 000000000..c45f748cb --- /dev/null +++ b/src/env.js @@ -0,0 +1,66 @@ +const { z } = require('zod'); +const { createEnv } = require('@t3-oss/env-nextjs'); + +const portSchema = z.string().regex(/\d+/).transform(Number).optional() +const envSchema = z.enum(["development", "test", "production"]); + +const env = createEnv({ + /** + * Specify your server-side environment variables schema here. This way you can ensure the app + * isn't built with invalid env vars. + */ + server: { + DATABASE_URL: z.string().url(), + NODE_ENV: envSchema, + NEXTAUTH_SECRET: + process.env.NODE_ENV === "production" + ? z.string().min(1) + : z.string().min(1).optional(), + NEXTAUTH_URL: z.preprocess( + // This makes Vercel deployments not fail if you don't set NEXTAUTH_URL + // Since NextAuth.js automatically uses the VERCEL_URL if present. + (str) => process.env.VERCEL_URL ?? str, + // VERCEL_URL doesn't include `https` so it cant be validated as a URL + process.env.VERCEL ? z.string().min(1) : z.string().url(), + ), + DEFAULT_COLOR_SCHEME: z.enum(['light', 'dark']).optional().default('light'), + DOCKER_HOST: z.string().optional(), + DOCKER_PORT: z.string().regex(/\d+/).transform(Number).optional(), + PORT: portSchema + }, + + /** + * Specify your client-side environment variables schema here. This way you can ensure the app + * isn't built with invalid env vars. To expose them to the client, prefix them with + * `NEXT_PUBLIC_`. + */ + client: { + // NEXT_PUBLIC_CLIENTVAR: z.string().min(1), + NEXT_PUBLIC_PORT: portSchema, + NEXT_PUBLIC_NODE_ENV: envSchema + }, + + /** + * You can't destruct `process.env` as a regular object in the Next.js edge runtimes (e.g. + * middlewares) or client-side so we need to destruct manually. + */ + runtimeEnv: { + DATABASE_URL: process.env.DATABASE_URL, + NODE_ENV: process.env.NODE_ENV, + NEXTAUTH_SECRET: process.env.NEXTAUTH_SECRET, + NEXTAUTH_URL: process.env.NEXTAUTH_URL, + NEXT_PUBLIC_DISABLE_EDIT_MODE: process.env.DISABLE_EDIT_MODE, + DISABLE_EDIT_MODE: process.env.DISABLE_EDIT_MODE, + DEFAULT_COLOR_SCHEME: process.env.DEFAULT_COLOR_SCHEME, + DOCKER_HOST: process.env.DOCKER_HOST, + DOCKER_PORT: process.env.DOCKER_PORT, + VERCEL_URL: process.env.VERCEL_URL, + PORT: process.env.PORT, + NEXT_PUBLIC_PORT: process.env.PORT, + NEXT_PUBLIC_NODE_ENV: process.env.NODE_ENV + }, +}); + +module.exports = { + env +} \ No newline at end of file diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 37ed1a412..3a21f1960 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -14,9 +14,10 @@ import { AppProps } from 'next/app'; import Head from 'next/head'; import { useEffect, useState } from 'react'; import 'video.js/dist/video-js.css'; +import { env } from '~/env.js'; import { api } from '~/utils/api'; -import nextI18nextConfig from '../../next-i18next.config'; +import nextI18nextConfig from '../../next-i18next.config.js'; import { ChangeAppPositionModal } from '../components/Dashboard/Modals/ChangePosition/ChangeAppPositionModal'; import { ChangeWidgetPositionModal } from '../components/Dashboard/Modals/ChangePosition/ChangeWidgetPositionModal'; import { EditAppModal } from '../components/Dashboard/Modals/EditAppModal/EditAppModal'; @@ -149,26 +150,22 @@ function App( } App.getInitialProps = ({ ctx }: { ctx: GetServerSidePropsContext }) => { - const disableEditMode = - process.env.DISABLE_EDIT_MODE && process.env.DISABLE_EDIT_MODE.toLowerCase() === 'true'; - if (disableEditMode) { + if (process.env.DISABLE_EDIT_MODE === 'true') { Consola.warn( 'EXPERIMENTAL: You have disabled the edit mode. Modifications are no longer possible and any requests on the API will be dropped. If you want to disable this, unset the DISABLE_EDIT_MODE environment variable. This behaviour may be removed in future versions of Homarr' ); } - if (process.env.DEFAULT_COLOR_SCHEME !== undefined) { - Consola.debug(`Overriding the default color scheme with ${process.env.DEFAULT_COLOR_SCHEME}`); + if (env.DEFAULT_COLOR_SCHEME !== 'light') { + Consola.debug(`Overriding the default color scheme with ${env.DEFAULT_COLOR_SCHEME}`); } - const colorScheme: ColorScheme = (process.env.DEFAULT_COLOR_SCHEME as ColorScheme) ?? 'light'; - return { pageProps: { colorScheme: getCookie('color-scheme', ctx) || 'light', packageAttributes: getServiceSidePackageAttributes(), - editModeEnabled: !disableEditMode, - defaultColorScheme: colorScheme, + editModeEnabled: process.env.DISABLE_EDIT_MODE !== 'true', + defaultColorScheme: env.DEFAULT_COLOR_SCHEME, }, }; }; diff --git a/src/pages/api/docker/DockerSingleton.ts b/src/pages/api/docker/DockerSingleton.ts index 9694059be..4602ed7d7 100644 --- a/src/pages/api/docker/DockerSingleton.ts +++ b/src/pages/api/docker/DockerSingleton.ts @@ -1,4 +1,5 @@ import Docker from 'dockerode'; +import { env } from '~/env'; export default class DockerSingleton extends Docker { private static dockerInstance: DockerSingleton; @@ -10,10 +11,8 @@ export default class DockerSingleton extends Docker { public static getInstance(): DockerSingleton { if (!DockerSingleton.dockerInstance) { DockerSingleton.dockerInstance = new Docker({ - // If env variable DOCKER_HOST is not set, it will use the default socket - ...(process.env.DOCKER_HOST && { host: process.env.DOCKER_HOST }), - // Same thing for docker port - ...(process.env.DOCKER_PORT && { port: process.env.DOCKER_PORT }), + host: env.DOCKER_HOST, + port: env.DOCKER_PORT, }); } return DockerSingleton.dockerInstance; diff --git a/src/pages/api/trpc/[trpc].ts b/src/pages/api/trpc/[trpc].ts index d3f7428f9..7bec97bf0 100644 --- a/src/pages/api/trpc/[trpc].ts +++ b/src/pages/api/trpc/[trpc].ts @@ -1,5 +1,6 @@ import { createNextApiHandler } from '@trpc/server/adapters/next'; import Consola from 'consola'; +import { env } from '~/env'; import { rootRouter } from '~/server/api/root'; import { createTRPCContext } from '~/server/api/trpc'; @@ -8,7 +9,7 @@ export default createNextApiHandler({ router: rootRouter, createContext: createTRPCContext, onError: - process.env.NODE_ENV === 'development' + env.NODE_ENV === 'development' ? ({ path, error }) => { Consola.error(`❌ tRPC failed on ${path ?? ''}: ${error.message}`); } diff --git a/src/server/api/routers/docker/DockerSingleton.ts b/src/server/api/routers/docker/DockerSingleton.ts index 9694059be..4602ed7d7 100644 --- a/src/server/api/routers/docker/DockerSingleton.ts +++ b/src/server/api/routers/docker/DockerSingleton.ts @@ -1,4 +1,5 @@ import Docker from 'dockerode'; +import { env } from '~/env'; export default class DockerSingleton extends Docker { private static dockerInstance: DockerSingleton; @@ -10,10 +11,8 @@ export default class DockerSingleton extends Docker { public static getInstance(): DockerSingleton { if (!DockerSingleton.dockerInstance) { DockerSingleton.dockerInstance = new Docker({ - // If env variable DOCKER_HOST is not set, it will use the default socket - ...(process.env.DOCKER_HOST && { host: process.env.DOCKER_HOST }), - // Same thing for docker port - ...(process.env.DOCKER_PORT && { port: process.env.DOCKER_PORT }), + host: env.DOCKER_HOST, + port: env.DOCKER_PORT, }); } return DockerSingleton.dockerInstance; diff --git a/src/tools/server/getPackageVersion.ts b/src/tools/server/getPackageVersion.ts index dfbed1606..cb7986d54 100644 --- a/src/tools/server/getPackageVersion.ts +++ b/src/tools/server/getPackageVersion.ts @@ -1,8 +1,10 @@ +import { env } from '~/env'; + import packageJson from '../../../package.json'; const getServerPackageVersion = (): string | undefined => packageJson.version; -const getServerNodeEnvironment = (): 'development' | 'production' | 'test' => process.env.NODE_ENV; +const getServerNodeEnvironment = () => env.NODE_ENV; const getDependencies = (): PackageJsonDependencies => packageJson.dependencies; diff --git a/src/utils/api.ts b/src/utils/api.ts index 7a3d03785..241c0af95 100644 --- a/src/utils/api.ts +++ b/src/utils/api.ts @@ -8,6 +8,7 @@ import { createTRPCProxyClient, httpBatchLink, loggerLink } from '@trpc/client'; import { createTRPCNext } from '@trpc/next'; import { type inferRouterInputs, type inferRouterOutputs } from '@trpc/server'; import superjson from 'superjson'; +import { env } from '~/env'; import { type RootRouter } from '~/server/api/root'; const getTrpcConfiguration = () => ({ @@ -26,7 +27,7 @@ const getTrpcConfiguration = () => ({ links: [ loggerLink({ enabled: (opts) => - process.env.NODE_ENV === 'development' || + env.NEXT_PUBLIC_NODE_ENV === 'development' || (opts.direction === 'down' && opts.result instanceof Error), }), httpBatchLink({ @@ -37,8 +38,7 @@ const getTrpcConfiguration = () => ({ const getBaseUrl = () => { if (typeof window !== 'undefined') return ''; // browser should use relative url - if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`; // SSR should use vercel url - return `http://localhost:${process.env.PORT ?? 3000}`; // dev SSR should use localhost + return `http://localhost:${env.NEXT_PUBLIC_PORT ?? 3000}`; // dev SSR should use localhost }; /** A set of type-safe react-query hooks for your tRPC API. */ diff --git a/yarn.lock b/yarn.lock index df67a75fe..19bb809af 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1714,6 +1714,28 @@ __metadata: languageName: node linkType: hard +"@t3-oss/env-core@npm:0.6.0": + version: 0.6.0 + resolution: "@t3-oss/env-core@npm:0.6.0" + peerDependencies: + typescript: ">=4.7.2" + zod: ^3.0.0 + checksum: 00c5b8e2d893f85e9d33099fded1e9ee1c74e642144b91d60096d31ed5bcd09986f14b275316568aa1a1f42d1b01a34b67dcf1396e11d837ff5c11b4bfb56a3a + languageName: node + linkType: hard + +"@t3-oss/env-nextjs@npm:^0.6.0": + version: 0.6.0 + resolution: "@t3-oss/env-nextjs@npm:0.6.0" + dependencies: + "@t3-oss/env-core": 0.6.0 + peerDependencies: + typescript: ">=4.7.2" + zod: ^3.0.0 + checksum: d3708558241bcf857dfcfbc778a4d0166a5e690414893d7a4eb95dcafa12810d4fdc1cffe41402004acdd0d8f558f9369499bd9032d04e158d8698b5e85c7f32 + languageName: node + linkType: hard + "@tabler/icons-react@npm:^2.18.0": version: 2.26.0 resolution: "@tabler/icons-react@npm:2.26.0" @@ -5537,6 +5559,7 @@ __metadata: "@nivo/core": ^0.83.0 "@nivo/line": ^0.83.0 "@react-native-async-storage/async-storage": ^1.18.1 + "@t3-oss/env-nextjs": ^0.6.0 "@tabler/icons-react": ^2.18.0 "@tanstack/query-async-storage-persister": ^4.27.1 "@tanstack/query-sync-storage-persister": ^4.27.1 From d7f6bdf417fc08815bfba819ccd874db8a8b2f72 Mon Sep 17 00:00:00 2001 From: Meier Lukas Date: Fri, 28 Jul 2023 18:51:44 +0200 Subject: [PATCH 002/233] =?UTF-8?q?=E2=9C=A8=20Add=20basic=20credentials?= =?UTF-8?q?=20authentication?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 10 +- prisma/db.sqlite | Bin 0 -> 53248 bytes prisma/schema.prisma | 63 +++++ src/pages/auth/[...nextauth].ts | 7 + src/server/api/trpc.ts | 41 ++- src/server/auth.ts | 197 ++++++++++++++ src/server/db.ts | 14 + src/utils/security.ts | 5 + src/utils/session.ts | 11 + src/validations/user.ts | 12 + yarn.lock | 446 +++++++++++++++++++++++++++++++- 11 files changed, 794 insertions(+), 12 deletions(-) create mode 100644 prisma/db.sqlite create mode 100644 prisma/schema.prisma create mode 100644 src/pages/auth/[...nextauth].ts create mode 100644 src/server/auth.ts create mode 100644 src/server/db.ts create mode 100644 src/utils/security.ts create mode 100644 src/utils/session.ts create mode 100644 src/validations/user.ts diff --git a/package.json b/package.json index 5d1a7761a..afd0e9cfd 100644 --- a/package.json +++ b/package.json @@ -39,8 +39,10 @@ "@mantine/modals": "^6.0.0", "@mantine/next": "^6.0.0", "@mantine/notifications": "^6.0.0", + "@next-auth/prisma-adapter": "^1.0.7", "@nivo/core": "^0.83.0", "@nivo/line": "^0.83.0", + "@prisma/client": "^5.0.0", "@react-native-async-storage/async-storage": "^1.18.1", "@t3-oss/env-nextjs": "^0.6.0", "@tabler/icons-react": "^2.18.0", @@ -53,9 +55,12 @@ "@trpc/next": "^10.29.1", "@trpc/react-query": "^10.29.1", "@trpc/server": "^10.29.1", + "@types/bcrypt": "^5.0.0", "@vitejs/plugin-react": "^4.0.0", "axios": "^1.0.0", + "bcrypt": "^5.1.0", "consola": "^3.0.0", + "cookies": "^0.8.0", "cookies-next": "^2.1.1", "dayjs": "^1.11.7", "dockerode": "^3.3.2", @@ -65,6 +70,7 @@ "i18next": "^22.5.1", "js-file-download": "^0.4.12", "next": "13.4.10", + "next-auth": "^4.22.3", "next-i18next": "^13.0.0", "nzbget-api": "^0.0.3", "prismjs": "^1.29.0", @@ -86,6 +92,7 @@ "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^14.0.0", "@trivago/prettier-plugin-sort-imports": "^4.2.0", + "@types/cookies": "^0.7.7", "@types/dockerode": "^3.3.9", "@types/node": "18.16.19", "@types/prismjs": "^1.26.0", @@ -107,6 +114,7 @@ "happy-dom": "^10.0.0", "node-mocks-http": "^1.12.2", "prettier": "^3.0.0", + "prisma": "^5.0.0", "sass": "^1.56.1", "ts-node": "latest", "turbo": "latest", @@ -220,4 +228,4 @@ ] } } -} \ No newline at end of file +} diff --git a/prisma/db.sqlite b/prisma/db.sqlite new file mode 100644 index 0000000000000000000000000000000000000000..e2af177f1f33b67707fbe3c3d957622674af9420 GIT binary patch literal 53248 zcmeI%J#X7a7{GBdar8nwM5}XTy%6S=(^5M##c?O)TyxJ{4sG5O zW;D84pBvl7T2lsmup!{hQC7Y%%0&;GLsP-@jY6%yqrZCdE*cjo%1jx-97wo?aO zKQA65ER!>iTATGZbK|3W^y^4S-iEARZ!(d>L8mM#lxvuIpS)k68Gzp z%mz7foDGe^U~(}Iob&1AwOowpVnH%oIoRO0Zi`twmd-J~wX_Ydbso<=rUYaJ9p4VLhxU36_zV>%)j=-PY4a_e4Cg zPE<5w_somWQU!7TMD12fVh?Y)j7`=0XYS9c2cv$sP^jJC(X}Y)X0J}=LxtmB1e564 z34?PE!@tGL!<)t0?ymmZchPKAGO5kh`kazXyUw)Ma&jF(#eE^eXvyiMEz#z6h#NPL zJ3YJGXmxC1s_2}!KW8Mf*%S|tyH@K_Cv+BjrW&d)y4FLhYjv7dPnaQtDJ%V1Q0*~0R#|0 z009ILKp;l~y#LS9Vi76=2q1s}0tg_000IagfB*s+1$h6T5sqdEAb; +interface CreateContextOptions { + session: Session | null; +} /** * This helper generates the "internals" for a tRPC context. If you need to use it, you can export @@ -31,7 +36,9 @@ type CreateContextOptions = Record; * * @see https://create.t3.gg/en/usage/trpc#-serverapitrpcts */ -const createInnerTRPCContext = (opts: CreateContextOptions) => ({}); +const createInnerTRPCContext = (opts: CreateContextOptions) => ({ + session: opts.session, +}); /** * This is the actual context you will use in your router. It will be used to process every request @@ -43,8 +50,11 @@ export const createTRPCContext = async (opts: CreateNextContextOptions) => { const { req, res } = opts; // Get the session from the server using the getServerSession wrapper function + const session = await getServerAuthSession({ req, res }); - return createInnerTRPCContext({}); + return createInnerTRPCContext({ + session, + }); }; /** @@ -90,3 +100,26 @@ export const createTRPCRouter = t.router; * are logged in. */ export const publicProcedure = t.procedure; + +/** Reusable middleware that enforces users are logged in before running the procedure. */ +const enforceUserIsAuthed = t.middleware(({ ctx, next }) => { + if (!ctx.session?.user) { + throw new TRPCError({ code: 'UNAUTHORIZED' }); + } + return next({ + ctx: { + // infers the `session` as non-nullable + session: { ...ctx.session, user: ctx.session.user }, + }, + }); +}); + +/** + * Protected (authenticated) procedure + * + * If you want a query or mutation to ONLY be accessible to logged in users, use this. It verifies + * the session is valid and guarantees `ctx.session.user` is not null. + * + * @see https://trpc.io/docs/procedures + */ +export const protectedProcedure = t.procedure.use(enforceUserIsAuthed); diff --git a/src/server/auth.ts b/src/server/auth.ts new file mode 100644 index 000000000..bfd95d7d6 --- /dev/null +++ b/src/server/auth.ts @@ -0,0 +1,197 @@ +import { PrismaAdapter } from '@next-auth/prisma-adapter'; +import bcrypt from 'bcrypt'; +import Cookies from 'cookies'; +import { type GetServerSidePropsContext, type NextApiRequest, type NextApiResponse } from 'next'; +import { type DefaultSession, type NextAuthOptions, getServerSession } from 'next-auth'; +import { decode, encode } from 'next-auth/jwt'; +import Credentials from 'next-auth/providers/credentials'; +import { prisma } from '~/server/db'; +import { fromDate, generateSessionToken } from '~/utils/session'; +import { signInSchema } from '~/validations/user'; + +/** + * Module augmentation for `next-auth` types. Allows us to add custom properties to the `session` + * object and keep type safety. + * + * @see https://next-auth.js.org/getting-started/typescript#module-augmentation + */ +declare module 'next-auth' { + interface Session extends DefaultSession { + user: DefaultSession['user'] & { + id: string; + isAdmin: boolean; + // ...other properties + // role: UserRole; + }; + } + + interface User { + isAdmin: boolean; + // ...other properties + // role: UserRole; + } +} + +declare module 'next-auth/jwt' { + interface JWT { + id: string; + isAdmin: boolean; + } +} + +const adapter = PrismaAdapter(prisma); +const sessionMaxAgeInSeconds = 30 * 24 * 60 * 60; // 30 days + +/** + * Options for NextAuth.js used to configure adapters, providers, callbacks, etc. + * + * @see https://next-auth.js.org/configuration/options + */ +export const constructAuthOptions = ( + req: NextApiRequest, + res: NextApiResponse +): NextAuthOptions => ({ + callbacks: { + async session({ session, user }) { + if (session.user) { + // eslint-disable-next-line no-param-reassign + session.user.id = user.id; + // eslint-disable-next-line no-param-reassign + session.user.name = user.name as string; + + const userFromDatabase = await prisma.user.findUniqueOrThrow({ + where: { + id: user.id, + }, + include: { + roles: true, + }, + }); + + session.user.isAdmin = userFromDatabase.isAdmin; + } + + return session; + }, + async signIn({ user }) { + // Check if this sign in callback is being called in the credentials authentication flow. + // If so, use the next-auth adapter to create a session entry in the database + // (SignIn is called after authorize so we can safely assume the user is valid and already authenticated). + if (!isCredentialsRequest(req)) return true; + + if (!user) return true; + + const sessionToken = generateSessionToken(); + const sessionExpiry = fromDate(sessionMaxAgeInSeconds); + + await adapter.createSession({ + sessionToken: sessionToken, + userId: user.id, + expires: sessionExpiry, + }); + + const cookies = new Cookies(req, res); + cookies.set('next-auth.session-token', sessionToken, { + expires: sessionExpiry, + }); + + return true; + }, + }, + session: { + strategy: 'database', + maxAge: sessionMaxAgeInSeconds, + }, + adapter: PrismaAdapter(prisma), + providers: [ + Credentials({ + name: 'credentials', + credentials: { + name: { + label: 'Username', + type: 'text', + }, + password: { label: 'Password', type: 'password' }, + }, + async authorize(credentials) { + const data = await signInSchema.parseAsync(credentials); + + const user = await prisma.user.findFirst({ + include: { + roles: true, + }, + where: { + name: data.name, + }, + }); + + if (!user || !user.password) { + return null; + } + + console.log(`user ${user.id} is trying to log in. checking password...`); + const isValidPassword = await bcrypt.compare(data.password, user.password); + + if (!isValidPassword) { + console.log(`password for user ${user.id} was incorrect`); + return null; + } + + console.log(`user ${user.id} successfully authorized`); + + return { + id: user.id, + name: user.name, + isAdmin: false, + }; + }, + }), + ], + jwt: { + async encode(params) { + if (!isCredentialsRequest(req)) { + return encode(params); + } + + const cookies = new Cookies(req, res); + const cookie = cookies.get('next-auth.session-token'); + return cookie ?? ''; + }, + + async decode(params) { + if (!isCredentialsRequest(req)) { + return decode(params); + } + + return null; + }, + }, +}); + +const isCredentialsRequest = (req: NextApiRequest): boolean => { + const nextAuthQueryParams = req.query.nextauth as ['callback', 'credentials']; + return ( + nextAuthQueryParams.includes('callback') && + nextAuthQueryParams.includes('credentials') && + req.method === 'POST' + ); +}; + +/** + * Wrapper for `getServerSession` so that you don't need to import the `authOptions` in every file. + * + * @see https://next-auth.js.org/configuration/nextjs + */ +export const getServerAuthSession = (ctx: { + req: GetServerSidePropsContext['req']; + res: GetServerSidePropsContext['res']; +}) => { + return getServerSession( + ctx.req, + ctx.res, + constructAuthOptions( + ctx.req as unknown as NextApiRequest, + ctx.res as unknown as NextApiResponse + ) + ); +}; diff --git a/src/server/db.ts b/src/server/db.ts new file mode 100644 index 000000000..963fba477 --- /dev/null +++ b/src/server/db.ts @@ -0,0 +1,14 @@ +import { PrismaClient } from '@prisma/client'; +import { env } from '~/env'; + +const globalForPrisma = globalThis as unknown as { + prisma: PrismaClient | undefined; +}; + +export const prisma = + globalForPrisma.prisma ?? + new PrismaClient({ + log: env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'], + }); + +if (env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma; diff --git a/src/utils/security.ts b/src/utils/security.ts new file mode 100644 index 000000000..395e9d29c --- /dev/null +++ b/src/utils/security.ts @@ -0,0 +1,5 @@ +import bcrypt from "bcrypt"; + +export const hashPassword = (password: string, salt: string) => { + return bcrypt.hashSync(password, salt); +}; diff --git a/src/utils/session.ts b/src/utils/session.ts new file mode 100644 index 000000000..976322467 --- /dev/null +++ b/src/utils/session.ts @@ -0,0 +1,11 @@ +import { randomUUID } from "crypto"; + +export const fromDate = (seconds: number, date = Date.now()) => { + return new Date(date + seconds * 1000); +}; + +// Helper functions to generate unique keys and calculate the expiry dates for session cookies +export const generateSessionToken = () => { + return randomUUID(); +}; + diff --git a/src/validations/user.ts b/src/validations/user.ts new file mode 100644 index 000000000..edb59445f --- /dev/null +++ b/src/validations/user.ts @@ -0,0 +1,12 @@ +import { z } from 'zod'; + +export const signInSchema = z.object({ + name: z.string(), + password: z.string(), +}); + +export const signUpFormSchema = z.object({ + username: z.string(), + password: z.string().min(8), + acceptTos: z.boolean(), +}); diff --git a/yarn.lock b/yarn.lock index c700e7e4c..636cd5b95 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1123,6 +1123,35 @@ __metadata: languageName: node linkType: hard +"@mapbox/node-pre-gyp@npm:^1.0.10": + version: 1.0.11 + resolution: "@mapbox/node-pre-gyp@npm:1.0.11" + dependencies: + detect-libc: ^2.0.0 + https-proxy-agent: ^5.0.0 + make-dir: ^3.1.0 + node-fetch: ^2.6.7 + nopt: ^5.0.0 + npmlog: ^5.0.1 + rimraf: ^3.0.2 + semver: ^7.3.5 + tar: ^6.1.11 + bin: + node-pre-gyp: bin/node-pre-gyp + checksum: b848f6abc531a11961d780db813cc510ca5a5b6bf3184d72134089c6875a91c44d571ba6c1879470020803f7803609e7b2e6e429651c026fe202facd11d444b8 + languageName: node + linkType: hard + +"@next-auth/prisma-adapter@npm:^1.0.7": + version: 1.0.7 + resolution: "@next-auth/prisma-adapter@npm:1.0.7" + peerDependencies: + "@prisma/client": ">=2.26.0 || >=3" + next-auth: ^4 + checksum: dea44185c0de109b4b2f0a9fd84c0cab40b248c70bc9f44af516d895d22442a69fd7d85f7894e47fa506f122a5c6631b7002b0371520970289eb9d7618709e06 + languageName: node + linkType: hard + "@next/bundle-analyzer@npm:^13.0.0": version: 13.4.10 resolution: "@next/bundle-analyzer@npm:13.4.10" @@ -1427,6 +1456,13 @@ __metadata: languageName: node linkType: hard +"@panva/hkdf@npm:^1.0.2": + version: 1.1.1 + resolution: "@panva/hkdf@npm:1.1.1" + checksum: f0dd12903751d8792420353f809ed3c7de860cf506399759fff5f59f7acfef8a77e2b64012898cee7e5b047708fa0bd91dff5ef55a502bf8ea11aad9842160da + languageName: node + linkType: hard + "@pkgr/utils@npm:^2.3.1": version: 2.4.1 resolution: "@pkgr/utils@npm:2.4.1" @@ -1448,6 +1484,34 @@ __metadata: languageName: node linkType: hard +"@prisma/client@npm:^5.0.0": + version: 5.0.0 + resolution: "@prisma/client@npm:5.0.0" + dependencies: + "@prisma/engines-version": 4.17.0-26.6b0aef69b7cdfc787f822ecd7cdc76d5f1991584 + peerDependencies: + prisma: "*" + peerDependenciesMeta: + prisma: + optional: true + checksum: 332c2af44e880ffc9dd1223992bf6f45910094c7a3a540cbbfdda45d6caf3e82998376338abdf85e34a12f1082ae932f2385d6396c62fe4bba7ec6b84de54466 + languageName: node + linkType: hard + +"@prisma/engines-version@npm:4.17.0-26.6b0aef69b7cdfc787f822ecd7cdc76d5f1991584": + version: 4.17.0-26.6b0aef69b7cdfc787f822ecd7cdc76d5f1991584 + resolution: "@prisma/engines-version@npm:4.17.0-26.6b0aef69b7cdfc787f822ecd7cdc76d5f1991584" + checksum: 8fcbceef3b554ee7fa404bead50be5286412a097b21723272aff298b289caf2802b01b84bb85c4c9f3b592eeac114c8d6e79db083f271dc8c54f453b4a515233 + languageName: node + linkType: hard + +"@prisma/engines@npm:5.0.0": + version: 5.0.0 + resolution: "@prisma/engines@npm:5.0.0" + checksum: 31271d85c29709059f91051d4cef7acf874014ba0128b674ca2f842e5fac61d3011e9db246dfa67ba4803081d36dbc9e31492716bab677128588343c92117b2b + languageName: node + linkType: hard + "@radix-ui/number@npm:1.0.0": version: 1.0.0 resolution: "@radix-ui/number@npm:1.0.0" @@ -1998,6 +2062,25 @@ __metadata: languageName: node linkType: hard +"@types/bcrypt@npm:^5.0.0": + version: 5.0.0 + resolution: "@types/bcrypt@npm:5.0.0" + dependencies: + "@types/node": "*" + checksum: 063c32c7a519d64768dfc0169a319b8244d6a6cb50a355c93992b3c5fee1dbc236526a1111f0e7bb25abc8b0473e5f40a5edfeb8b33cad2a6ea35aa2d7d7db14 + languageName: node + linkType: hard + +"@types/body-parser@npm:*": + version: 1.19.2 + resolution: "@types/body-parser@npm:1.19.2" + dependencies: + "@types/connect": "*" + "@types/node": "*" + checksum: e17840c7d747a549f00aebe72c89313d09fbc4b632b949b2470c5cb3b1cb73863901ae84d9335b567a79ec5efcfb8a28ff8e3f36bc8748a9686756b6d5681f40 + languageName: node + linkType: hard + "@types/cacheable-request@npm:^6.0.1": version: 6.0.3 resolution: "@types/cacheable-request@npm:6.0.3" @@ -2026,6 +2109,15 @@ __metadata: languageName: node linkType: hard +"@types/connect@npm:*": + version: 3.4.35 + resolution: "@types/connect@npm:3.4.35" + dependencies: + "@types/node": "*" + checksum: fe81351470f2d3165e8b12ce33542eef89ea893e36dd62e8f7d72566dfb7e448376ae962f9f3ea888547ce8b55a40020ca0e01d637fab5d99567673084542641 + languageName: node + linkType: hard + "@types/cookie@npm:^0.4.1": version: 0.4.1 resolution: "@types/cookie@npm:0.4.1" @@ -2033,6 +2125,18 @@ __metadata: languageName: node linkType: hard +"@types/cookies@npm:^0.7.7": + version: 0.7.7 + resolution: "@types/cookies@npm:0.7.7" + dependencies: + "@types/connect": "*" + "@types/express": "*" + "@types/keygrip": "*" + "@types/node": "*" + checksum: d3759efc1182cb0651808570ae13638677b67b0ea724eef7b174e58ffe6ea044b62c7c2715e532f76f88fce4dd8101ed32ac6fbb73226db654017924e8a2a1e6 + languageName: node + linkType: hard + "@types/d3-color@npm:^2.0.0": version: 2.0.3 resolution: "@types/d3-color@npm:2.0.3" @@ -2134,6 +2238,30 @@ __metadata: languageName: node linkType: hard +"@types/express-serve-static-core@npm:^4.17.33": + version: 4.17.35 + resolution: "@types/express-serve-static-core@npm:4.17.35" + dependencies: + "@types/node": "*" + "@types/qs": "*" + "@types/range-parser": "*" + "@types/send": "*" + checksum: cc8995d10c6feda475ec1b3a0e69eb0f35f21ab6b49129ad5c6f279e0bc5de8175bc04ec51304cb79a43eec3ed2f5a1e01472eb6d5f827b8c35c6ca8ad24eb6e + languageName: node + linkType: hard + +"@types/express@npm:*": + version: 4.17.17 + resolution: "@types/express@npm:4.17.17" + dependencies: + "@types/body-parser": "*" + "@types/express-serve-static-core": ^4.17.33 + "@types/qs": "*" + "@types/serve-static": "*" + checksum: 0196dacc275ac3ce89d7364885cb08e7fb61f53ca101f65886dbf1daf9b7eb05c0943e2e4bbd01b0cc5e50f37e0eea7e4cbe97d0304094411ac73e1b7998f4da + languageName: node + linkType: hard + "@types/hoist-non-react-statics@npm:^3.3.1": version: 3.3.1 resolution: "@types/hoist-non-react-statics@npm:3.3.1" @@ -2151,6 +2279,13 @@ __metadata: languageName: node linkType: hard +"@types/http-errors@npm:*": + version: 2.0.1 + resolution: "@types/http-errors@npm:2.0.1" + checksum: 3bb0c50b0a652e679a84c30cd0340d696c32ef6558518268c238840346c077f899315daaf1c26c09c57ddd5dc80510f2a7f46acd52bf949e339e35ed3ee9654f + languageName: node + linkType: hard + "@types/istanbul-lib-coverage@npm:*, @types/istanbul-lib-coverage@npm:^2.0.0, @types/istanbul-lib-coverage@npm:^2.0.1": version: 2.0.4 resolution: "@types/istanbul-lib-coverage@npm:2.0.4" @@ -2200,6 +2335,13 @@ __metadata: languageName: node linkType: hard +"@types/keygrip@npm:*": + version: 1.0.2 + resolution: "@types/keygrip@npm:1.0.2" + checksum: 60bc2738a4f107070ee3d96f44709cb38f3a96c7ccabab09f56c1b2b4d85f869fd8fb9f1f2937e863d0e9e781f005c2223b823bf32b859185b4f52370c352669 + languageName: node + linkType: hard + "@types/keyv@npm:^3.1.4": version: 3.1.4 resolution: "@types/keyv@npm:3.1.4" @@ -2209,6 +2351,20 @@ __metadata: languageName: node linkType: hard +"@types/mime@npm:*": + version: 3.0.1 + resolution: "@types/mime@npm:3.0.1" + checksum: 4040fac73fd0cea2460e29b348c1a6173da747f3a87da0dbce80dd7a9355a3d0e51d6d9a401654f3e5550620e3718b5a899b2ec1debf18424e298a2c605346e7 + languageName: node + linkType: hard + +"@types/mime@npm:^1": + version: 1.3.2 + resolution: "@types/mime@npm:1.3.2" + checksum: 0493368244cced1a69cb791b485a260a422e6fcc857782e1178d1e6f219f1b161793e9f87f5fae1b219af0f50bee24fcbe733a18b4be8fdd07a38a8fb91146fd + languageName: node + linkType: hard + "@types/node@npm:*": version: 20.2.5 resolution: "@types/node@npm:20.2.5" @@ -2258,6 +2414,20 @@ __metadata: languageName: node linkType: hard +"@types/qs@npm:*": + version: 6.9.7 + resolution: "@types/qs@npm:6.9.7" + checksum: 7fd6f9c25053e9b5bb6bc9f9f76c1d89e6c04f7707a7ba0e44cc01f17ef5284adb82f230f542c2d5557d69407c9a40f0f3515e8319afd14e1e16b5543ac6cdba + languageName: node + linkType: hard + +"@types/range-parser@npm:*": + version: 1.2.4 + resolution: "@types/range-parser@npm:1.2.4" + checksum: b7c0dfd5080a989d6c8bb0b6750fc0933d9acabeb476da6fe71d8bdf1ab65e37c136169d84148034802f48378ab94e3c37bb4ef7656b2bec2cb9c0f8d4146a95 + languageName: node + linkType: hard + "@types/react-dom@npm:^18.0.0": version: 18.2.4 resolution: "@types/react-dom@npm:18.2.4" @@ -2310,6 +2480,27 @@ __metadata: languageName: node linkType: hard +"@types/send@npm:*": + version: 0.17.1 + resolution: "@types/send@npm:0.17.1" + dependencies: + "@types/mime": ^1 + "@types/node": "*" + checksum: 10b620a5960058ef009afbc17686f680d6486277c62f640845381ec4baa0ea683fdd77c3afea4803daf5fcddd3fb2972c8aa32e078939f1d4e96f83195c89793 + languageName: node + linkType: hard + +"@types/serve-static@npm:*": + version: 1.15.2 + resolution: "@types/serve-static@npm:1.15.2" + dependencies: + "@types/http-errors": "*" + "@types/mime": "*" + "@types/node": "*" + checksum: 15c261dbfc57890f7cc17c04d5b22b418dfa0330c912b46c5d8ae2064da5d6f844ef7f41b63c7f4bbf07675e97ebe6ac804b032635ec742ae45d6f1274259b3e + languageName: node + linkType: hard + "@types/ssh2@npm:*": version: 1.11.11 resolution: "@types/ssh2@npm:1.11.11" @@ -2772,7 +2963,7 @@ __metadata: languageName: node linkType: hard -"abbrev@npm:^1.0.0": +"abbrev@npm:1, abbrev@npm:^1.0.0": version: 1.1.1 resolution: "abbrev@npm:1.1.1" checksum: a4a97ec07d7ea112c517036882b2ac22f3109b7b19077dc656316d07d308438aac28e4d9746dc4d84bf6b1e75b4a7b0a5f3cb30592419f128ca9a8cee3bcfa17 @@ -2926,6 +3117,16 @@ __metadata: languageName: node linkType: hard +"are-we-there-yet@npm:^2.0.0": + version: 2.0.0 + resolution: "are-we-there-yet@npm:2.0.0" + dependencies: + delegates: ^1.0.0 + readable-stream: ^3.6.0 + checksum: 6c80b4fd04ecee6ba6e737e0b72a4b41bdc64b7d279edfc998678567ff583c8df27e27523bc789f2c99be603ffa9eaa612803da1d886962d2086e7ff6fa90c7c + languageName: node + linkType: hard + "are-we-there-yet@npm:^3.0.0": version: 3.0.1 resolution: "are-we-there-yet@npm:3.0.1" @@ -3140,6 +3341,16 @@ __metadata: languageName: node linkType: hard +"bcrypt@npm:^5.1.0": + version: 5.1.0 + resolution: "bcrypt@npm:5.1.0" + dependencies: + "@mapbox/node-pre-gyp": ^1.0.10 + node-addon-api: ^5.0.0 + checksum: a590b65d276d75d861dc85acc3128508b8f78c87431719658ea3be7996368b34b397b6efefe6bca0a3d555bf41a9267307fd4ce04e956598fca3ba81199c6706 + languageName: node + linkType: hard + "big-integer@npm:^1.6.44": version: 1.6.51 resolution: "big-integer@npm:1.6.51" @@ -3547,7 +3758,7 @@ __metadata: languageName: node linkType: hard -"color-support@npm:^1.1.3": +"color-support@npm:^1.1.2, color-support@npm:^1.1.3": version: 1.1.3 resolution: "color-support@npm:1.1.3" bin: @@ -3600,7 +3811,7 @@ __metadata: languageName: node linkType: hard -"console-control-strings@npm:^1.1.0": +"console-control-strings@npm:^1.0.0, console-control-strings@npm:^1.1.0": version: 1.1.0 resolution: "console-control-strings@npm:1.1.0" checksum: 8755d76787f94e6cf79ce4666f0c5519906d7f5b02d4b884cf41e11dcd759ed69c57da0670afd9236d229a46e0f9cf519db0cd829c6dca820bb5a5c3def584ed @@ -3630,6 +3841,13 @@ __metadata: languageName: node linkType: hard +"cookie@npm:^0.5.0": + version: 0.5.0 + resolution: "cookie@npm:0.5.0" + checksum: 1f4bd2ca5765f8c9689a7e8954183f5332139eb72b6ff783d8947032ec1fdf43109852c178e21a953a30c0dd42257828185be01b49d1eb1a67fd054ca588a180 + languageName: node + linkType: hard + "cookies-next@npm:^2.1.1": version: 2.1.2 resolution: "cookies-next@npm:2.1.2" @@ -3641,6 +3859,16 @@ __metadata: languageName: node linkType: hard +"cookies@npm:^0.8.0": + version: 0.8.0 + resolution: "cookies@npm:0.8.0" + dependencies: + depd: ~2.0.0 + keygrip: ~1.1.0 + checksum: 806055a44f128705265b1bc6a853058da18bf80dea3654ad99be20985b1fa1b14f86c1eef73644aab8071241f8a78acd57202b54c4c5c70769fc694fbb9c4edc + languageName: node + linkType: hard + "copy-anything@npm:^3.0.2": version: 3.0.5 resolution: "copy-anything@npm:3.0.5" @@ -4029,13 +4257,20 @@ __metadata: languageName: node linkType: hard -"depd@npm:^2.0.0": +"depd@npm:^2.0.0, depd@npm:~2.0.0": version: 2.0.0 resolution: "depd@npm:2.0.0" checksum: abbe19c768c97ee2eed6282d8ce3031126662252c58d711f646921c9623f9052e3e1906443066beec1095832f534e57c523b7333f8e7e0d93051ab6baef5ab3a languageName: node linkType: hard +"detect-libc@npm:^2.0.0": + version: 2.0.2 + resolution: "detect-libc@npm:2.0.2" + checksum: 2b2cd3649b83d576f4be7cc37eb3b1815c79969c8b1a03a40a4d55d83bc74d010753485753448eacb98784abf22f7dbd3911fd3b60e29fda28fed2d1a997944d + languageName: node + linkType: hard + "detect-node-es@npm:^1.1.0": version: 1.1.0 resolution: "detect-node-es@npm:1.1.0" @@ -5140,6 +5375,23 @@ __metadata: languageName: node linkType: hard +"gauge@npm:^3.0.0": + version: 3.0.2 + resolution: "gauge@npm:3.0.2" + dependencies: + aproba: ^1.0.3 || ^2.0.0 + color-support: ^1.1.2 + console-control-strings: ^1.0.0 + has-unicode: ^2.0.1 + object-assign: ^4.1.1 + signal-exit: ^3.0.0 + string-width: ^4.2.3 + strip-ansi: ^6.0.1 + wide-align: ^1.1.2 + checksum: 81296c00c7410cdd48f997800155fbead4f32e4f82109be0719c63edc8560e6579946cc8abd04205297640691ec26d21b578837fd13a4e96288ab4b40b1dc3e9 + languageName: node + linkType: hard + "gauge@npm:^4.0.3": version: 4.0.4 resolution: "gauge@npm:4.0.4" @@ -5554,10 +5806,12 @@ __metadata: "@mantine/modals": ^6.0.0 "@mantine/next": ^6.0.0 "@mantine/notifications": ^6.0.0 + "@next-auth/prisma-adapter": ^1.0.7 "@next/bundle-analyzer": ^13.0.0 "@next/eslint-plugin-next": ^13.4.5 "@nivo/core": ^0.83.0 "@nivo/line": ^0.83.0 + "@prisma/client": ^5.0.0 "@react-native-async-storage/async-storage": ^1.18.1 "@t3-oss/env-nextjs": ^0.6.0 "@tabler/icons-react": ^2.18.0 @@ -5573,6 +5827,8 @@ __metadata: "@trpc/next": ^10.29.1 "@trpc/react-query": ^10.29.1 "@trpc/server": ^10.29.1 + "@types/bcrypt": ^5.0.0 + "@types/cookies": ^0.7.7 "@types/dockerode": ^3.3.9 "@types/node": 18.16.19 "@types/prismjs": ^1.26.0 @@ -5585,7 +5841,9 @@ __metadata: "@vitest/coverage-c8": ^0.33.0 "@vitest/ui": ^0.33.0 axios: ^1.0.0 + bcrypt: ^5.1.0 consola: ^3.0.0 + cookies: ^0.8.0 cookies-next: ^2.1.1 dayjs: ^1.11.7 dockerode: ^3.3.2 @@ -5604,10 +5862,12 @@ __metadata: i18next: ^22.5.1 js-file-download: ^0.4.12 next: 13.4.10 + next-auth: ^4.22.3 next-i18next: ^13.0.0 node-mocks-http: ^1.12.2 nzbget-api: ^0.0.3 prettier: ^3.0.0 + prisma: ^5.0.0 prismjs: ^1.29.0 react: ^18.2.0 react-dom: ^18.2.0 @@ -6347,6 +6607,13 @@ __metadata: languageName: node linkType: hard +"jose@npm:^4.11.4, jose@npm:^4.14.4": + version: 4.14.4 + resolution: "jose@npm:4.14.4" + checksum: 2d820a91a8fd97c05d8bc8eedc373b944a0cd7f5fe41063086da233d0473c73fb523912a9f026ea870782bd221f4a515f441a2d3af4de48c6f2c76dac5082377 + languageName: node + linkType: hard + "js-file-download@npm:^0.4.12": version: 0.4.12 resolution: "js-file-download@npm:0.4.12" @@ -6453,6 +6720,15 @@ __metadata: languageName: node linkType: hard +"keygrip@npm:~1.1.0": + version: 1.1.0 + resolution: "keygrip@npm:1.1.0" + dependencies: + tsscmp: 1.0.6 + checksum: 078cd16a463d187121f0a27c1c9c95c52ad392b620f823431689f345a0501132cee60f6e96914b07d570105af470b96960402accd6c48a0b1f3cd8fac4fa2cae + languageName: node + linkType: hard + "keyv@npm:^4.0.0, keyv@npm:^4.5.2": version: 4.5.2 resolution: "keyv@npm:4.5.2" @@ -6620,7 +6896,7 @@ __metadata: languageName: node linkType: hard -"make-dir@npm:^3.0.0": +"make-dir@npm:^3.0.0, make-dir@npm:^3.1.0": version: 3.1.0 resolution: "make-dir@npm:3.1.0" dependencies: @@ -7028,6 +7304,31 @@ __metadata: languageName: node linkType: hard +"next-auth@npm:^4.22.3": + version: 4.22.3 + resolution: "next-auth@npm:4.22.3" + dependencies: + "@babel/runtime": ^7.20.13 + "@panva/hkdf": ^1.0.2 + cookie: ^0.5.0 + jose: ^4.11.4 + oauth: ^0.9.15 + openid-client: ^5.4.0 + preact: ^10.6.3 + preact-render-to-string: ^5.1.19 + uuid: ^8.3.2 + peerDependencies: + next: ^12.2.5 || ^13 + nodemailer: ^6.6.5 + react: ^17.0.2 || ^18 + react-dom: ^17.0.2 || ^18 + peerDependenciesMeta: + nodemailer: + optional: true + checksum: 2ab79f91c417320a514250a8ccd1753fe0779992b9d4c949c22704f2375ec30a221c72a0e817bb6372ca422c56a0a84280f6b8d97156265370ef9ca7d5636624 + languageName: node + linkType: hard + "next-i18next@npm:^13.0.0": version: 13.3.0 resolution: "next-i18next@npm:13.3.0" @@ -7105,6 +7406,15 @@ __metadata: languageName: node linkType: hard +"node-addon-api@npm:^5.0.0": + version: 5.1.0 + resolution: "node-addon-api@npm:5.1.0" + dependencies: + node-gyp: latest + checksum: 2508bd2d2981945406243a7bd31362fc7af8b70b8b4d65f869c61731800058fb818cc2fd36c8eac714ddd0e568cc85becf5e165cebbdf7b5024d5151bbc75ea1 + languageName: node + linkType: hard + "node-domexception@npm:1.0.0": version: 1.0.0 resolution: "node-domexception@npm:1.0.0" @@ -7126,6 +7436,20 @@ __metadata: languageName: node linkType: hard +"node-fetch@npm:^2.6.7": + version: 2.6.12 + resolution: "node-fetch@npm:2.6.12" + dependencies: + whatwg-url: ^5.0.0 + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + checksum: 3bc1655203d47ee8e313c0d96664b9673a3d4dd8002740318e9d27d14ef306693a4b2ef8d6525775056fd912a19e23f3ac0d7111ad8925877b7567b29a625592 + languageName: node + linkType: hard + "node-gyp@npm:latest": version: 9.3.1 resolution: "node-gyp@npm:9.3.1" @@ -7178,6 +7502,17 @@ __metadata: languageName: node linkType: hard +"nopt@npm:^5.0.0": + version: 5.0.0 + resolution: "nopt@npm:5.0.0" + dependencies: + abbrev: 1 + bin: + nopt: bin/nopt.js + checksum: d35fdec187269503843924e0114c0c6533fb54bbf1620d0f28b4b60ba01712d6687f62565c55cc20a504eff0fbe5c63e22340c3fad549ad40469ffb611b04f2f + languageName: node + linkType: hard + "nopt@npm:^6.0.0": version: 6.0.0 resolution: "nopt@npm:6.0.0" @@ -7228,6 +7563,18 @@ __metadata: languageName: node linkType: hard +"npmlog@npm:^5.0.1": + version: 5.0.1 + resolution: "npmlog@npm:5.0.1" + dependencies: + are-we-there-yet: ^2.0.0 + console-control-strings: ^1.1.0 + gauge: ^3.0.0 + set-blocking: ^2.0.0 + checksum: 516b2663028761f062d13e8beb3f00069c5664925871a9b57989642ebe09f23ab02145bf3ab88da7866c4e112cafff72401f61a672c7c8a20edc585a7016ef5f + languageName: node + linkType: hard + "npmlog@npm:^6.0.0": version: 6.0.2 resolution: "npmlog@npm:6.0.2" @@ -7251,6 +7598,13 @@ __metadata: languageName: node linkType: hard +"oauth@npm:^0.9.15": + version: 0.9.15 + resolution: "oauth@npm:0.9.15" + checksum: 957c0d8d85300398dcb0e293953650c0fc3facc795bee8228238414f19f59cef5fd4ee8d17a972c142924c10c5f6ec50ef80f77f4a6cc6e3c98f9d22c027801c + languageName: node + linkType: hard + "object-assign@npm:^4.1.0, object-assign@npm:^4.1.1": version: 4.1.1 resolution: "object-assign@npm:4.1.1" @@ -7258,6 +7612,13 @@ __metadata: languageName: node linkType: hard +"object-hash@npm:^2.2.0": + version: 2.2.0 + resolution: "object-hash@npm:2.2.0" + checksum: 55ba841e3adce9c4f1b9b46b41983eda40f854e0d01af2802d3ae18a7085a17168d6b81731d43fdf1d6bcbb3c9f9c56d22c8fea992203ad90a38d7d919bc28f1 + languageName: node + linkType: hard + "object-inspect@npm:^1.12.3, object-inspect@npm:^1.9.0": version: 1.12.3 resolution: "object-inspect@npm:1.12.3" @@ -7344,6 +7705,13 @@ __metadata: languageName: node linkType: hard +"oidc-token-hash@npm:^5.0.3": + version: 5.0.3 + resolution: "oidc-token-hash@npm:5.0.3" + checksum: 35fa19aea9ff2c509029ec569d74b778c8a215b92bd5e6e9bc4ebbd7ab035f44304ff02430a6397c3fb7c1d15ebfa467807ca0bcd31d06ba610b47798287d303 + languageName: node + linkType: hard + "once@npm:^1.3.0, once@npm:^1.3.1, once@npm:^1.4.0": version: 1.4.0 resolution: "once@npm:1.4.0" @@ -7392,6 +7760,18 @@ __metadata: languageName: node linkType: hard +"openid-client@npm:^5.4.0": + version: 5.4.3 + resolution: "openid-client@npm:5.4.3" + dependencies: + jose: ^4.14.4 + lru-cache: ^6.0.0 + object-hash: ^2.2.0 + oidc-token-hash: ^5.0.3 + checksum: 0e5a126b77dad0320e8f7023ac7ad7f5f1f82ad5f985f7ab0b42a7cf36700dfb78f0bef9b59c1fae915dce0148ef191b49921cd0a01443b64c04f862d9dc03e0 + languageName: node + linkType: hard + "optionator@npm:^0.9.3": version: 0.9.3 resolution: "optionator@npm:0.9.3" @@ -7598,6 +7978,24 @@ __metadata: languageName: node linkType: hard +"preact-render-to-string@npm:^5.1.19": + version: 5.2.6 + resolution: "preact-render-to-string@npm:5.2.6" + dependencies: + pretty-format: ^3.8.0 + peerDependencies: + preact: ">=10" + checksum: be8d5d8fb502d422c503e68af7bcccb6facd942f3ae9a4d093ebe3f1d4f0b15c540624bdac434d53a2a8e8fb7afa4606383414e937c40933ca43445470a026ff + languageName: node + linkType: hard + +"preact@npm:^10.6.3": + version: 10.16.0 + resolution: "preact@npm:10.16.0" + checksum: 47a91f47d583b68a4afe971a7f992c06547df6d637cadf56eb3b69fee1fb202659b199af37d0e1a90637385144cadd75aa40acdb4e125cc4b3155e2883c24c07 + languageName: node + linkType: hard + "prelude-ls@npm:^1.2.1": version: 1.2.1 resolution: "prelude-ls@npm:1.2.1" @@ -7636,6 +8034,24 @@ __metadata: languageName: node linkType: hard +"pretty-format@npm:^3.8.0": + version: 3.8.0 + resolution: "pretty-format@npm:3.8.0" + checksum: 21a114d43ef06978f8f7f6212be4649b0b094f05d9b30e14e37550bf35c8ca24d8adbca9e5adc4cc15d9eaf7a1e7a30478a4dc37b30982bfdf0292a5b385484c + languageName: node + linkType: hard + +"prisma@npm:^5.0.0": + version: 5.0.0 + resolution: "prisma@npm:5.0.0" + dependencies: + "@prisma/engines": 5.0.0 + bin: + prisma: build/index.js + checksum: fdc62377853d25b4db664c736fd0b08d2b0c6db5752e6f6c6ec3bda77634cfb79e6f49d52d4b8f54ddb8ec9c28fc3fb0c13f95caf61085447d0929e258af9284 + languageName: node + linkType: hard + "prismjs@npm:^1.29.0": version: 1.29.0 resolution: "prismjs@npm:1.29.0" @@ -8348,7 +8764,7 @@ __metadata: languageName: node linkType: hard -"signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.3, signal-exit@npm:^3.0.7": +"signal-exit@npm:^3.0.0, signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.3, signal-exit@npm:^3.0.7": version: 3.0.7 resolution: "signal-exit@npm:3.0.7" checksum: a2f098f247adc367dffc27845853e9959b9e88b01cb301658cfe4194352d8d2bb32e18467c786a7fe15f1d44b233ea35633d076d5e737870b7139949d1ab6318 @@ -8982,6 +9398,13 @@ __metadata: languageName: node linkType: hard +"tsscmp@npm:1.0.6": + version: 1.0.6 + resolution: "tsscmp@npm:1.0.6" + checksum: 1512384def36bccc9125cabbd4c3b0e68608d7ee08127ceaa0b84a71797263f1a01c7f82fa69be8a3bd3c1396e2965d2f7b52d581d3a5eeaf3967fbc52e3b3bf + languageName: node + linkType: hard + "tsutils@npm:^3.21.0": version: 3.21.0 resolution: "tsutils@npm:3.21.0" @@ -9308,6 +9731,15 @@ __metadata: languageName: node linkType: hard +"uuid@npm:^8.3.2": + version: 8.3.2 + resolution: "uuid@npm:8.3.2" + bin: + uuid: dist/bin/uuid + checksum: 5575a8a75c13120e2f10e6ddc801b2c7ed7d8f3c8ac22c7ed0c7b2ba6383ec0abda88c905085d630e251719e0777045ae3236f04c812184b7c765f63a70e58df + languageName: node + linkType: hard + "uuid@npm:^9.0.0": version: 9.0.0 resolution: "uuid@npm:9.0.0" @@ -9669,7 +10101,7 @@ __metadata: languageName: node linkType: hard -"wide-align@npm:^1.1.5": +"wide-align@npm:^1.1.2, wide-align@npm:^1.1.5": version: 1.1.5 resolution: "wide-align@npm:1.1.5" dependencies: From 326395730ebaa99e4c2ef01d3bb9fd8d5e20cd62 Mon Sep 17 00:00:00 2001 From: Meier Lukas Date: Fri, 28 Jul 2023 20:45:54 +0200 Subject: [PATCH 003/233] =?UTF-8?q?=F0=9F=90=9B=20Fix=20issues=20with=20lo?= =?UTF-8?q?gin=20page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 4 +- prisma/db.sqlite | Bin 53248 -> 0 bytes public/locales/en/authentication/login.json | 23 +- src/pages/{ => api}/auth/[...nextauth].ts | 0 src/pages/login.tsx | 160 +- src/server/auth.ts | 12 +- src/utils/empty-provider.ts | 14 + yarn.lock | 1888 ++++++++++--------- 8 files changed, 1104 insertions(+), 997 deletions(-) delete mode 100644 prisma/db.sqlite rename src/pages/{ => api}/auth/[...nextauth].ts (100%) create mode 100644 src/utils/empty-provider.ts diff --git a/package.json b/package.json index afd0e9cfd..25becea57 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "@mantine/modals": "^6.0.0", "@mantine/next": "^6.0.0", "@mantine/notifications": "^6.0.0", - "@next-auth/prisma-adapter": "^1.0.7", + "@next-auth/prisma-adapter": "^1.0.5", "@nivo/core": "^0.83.0", "@nivo/line": "^0.83.0", "@prisma/client": "^5.0.0", @@ -70,7 +70,7 @@ "i18next": "^22.5.1", "js-file-download": "^0.4.12", "next": "13.4.10", - "next-auth": "^4.22.3", + "next-auth": "^4.21.0", "next-i18next": "^13.0.0", "nzbget-api": "^0.0.3", "prismjs": "^1.29.0", diff --git a/prisma/db.sqlite b/prisma/db.sqlite deleted file mode 100644 index e2af177f1f33b67707fbe3c3d957622674af9420..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 53248 zcmeI%J#X7a7{GBdar8nwM5}XTy%6S=(^5M##c?O)TyxJ{4sG5O zW;D84pBvl7T2lsmup!{hQC7Y%%0&;GLsP-@jY6%yqrZCdE*cjo%1jx-97wo?aO zKQA65ER!>iTATGZbK|3W^y^4S-iEARZ!(d>L8mM#lxvuIpS)k68Gzp z%mz7foDGe^U~(}Iob&1AwOowpVnH%oIoRO0Zi`twmd-J~wX_Ydbso<=rUYaJ9p4VLhxU36_zV>%)j=-PY4a_e4Cg zPE<5w_somWQU!7TMD12fVh?Y)j7`=0XYS9c2cv$sP^jJC(X}Y)X0J}=LxtmB1e564 z34?PE!@tGL!<)t0?ymmZchPKAGO5kh`kazXyUw)Ma&jF(#eE^eXvyiMEz#z6h#NPL zJ3YJGXmxC1s_2}!KW8Mf*%S|tyH@K_Cv+BjrW&d)y4FLhYjv7dPnaQtDJ%V1Q0*~0R#|0 z009ILKp;l~y#LS9Vi76=2q1s}0tg_000IagfB*s+1$h6T5sqdEAb>({ + validateInputOnChange: true, + validateInputOnBlur: true, + validate: zodResolver(signInSchema), }); + + const handleSubmit = (values: z.infer) => { + signIn('credentials', { + redirect: true, + name: values.name, + password: values.password, + callbackUrl: '/', + }); + }; + return ( - - - ({ fontFamily: `Greycliff CF, ${theme.fontFamily}`, fontWeight: 900 })} - > + <Flex h="100dvh" display="flex" w="100%" direction="column" align="center" justify="center"> + <Card withBorder shadow="md" p="xl" radius="md" w="90%" maw={420}> + <Title align="center" weight={900}> {t('title')} - + {t('text')} -
{ - setCookie('password', values.password, { - maxAge: 60 * 60 * 24 * 30, - sameSite: 'lax', - }); - showNotification({ - id: 'load-data', - loading: true, - title: t('notifications.checking.title'), - message: t('notifications.checking.message'), - autoClose: false, - withCloseButton: false, - }); - axios - .post('/api/configs/tryPassword', { - tried: values.password, - }) - .then((res) => { - setTimeout(() => { - if (res.data.success === true) { - router.push('/'); - updateNotification({ - id: 'load-data', - color: 'teal', - title: t('notifications.correct.title'), - message: undefined, - icon: , - autoClose: 1000, - }); - } - if (res.data.success === false) { - updateNotification({ - id: 'load-data', - color: 'red', - title: t('notifications.wrong.title'), - message: undefined, - icon: , - autoClose: 2000, - }); - } - }, 500); - }); - })} - > - - + + + + + + + + + + {queryParams.error === 'CredentialsSignin' && ( + } color="red"> + {t('alert')} + + )} + -
-
+ + ); } diff --git a/src/server/auth.ts b/src/server/auth.ts index bfd95d7d6..70f593c1c 100644 --- a/src/server/auth.ts +++ b/src/server/auth.ts @@ -6,6 +6,7 @@ import { type DefaultSession, type NextAuthOptions, getServerSession } from 'nex import { decode, encode } from 'next-auth/jwt'; import Credentials from 'next-auth/providers/credentials'; import { prisma } from '~/server/db'; +import EmptyNextAuthProvider from '~/utils/empty-provider'; import { fromDate, generateSessionToken } from '~/utils/session'; import { signInSchema } from '~/validations/user'; @@ -63,9 +64,6 @@ export const constructAuthOptions = ( where: { id: user.id, }, - include: { - roles: true, - }, }); session.user.isAdmin = userFromDatabase.isAdmin; @@ -102,6 +100,10 @@ export const constructAuthOptions = ( strategy: 'database', maxAge: sessionMaxAgeInSeconds, }, + pages: { + signIn: '/login', + error: '/login', + }, adapter: PrismaAdapter(prisma), providers: [ Credentials({ @@ -117,9 +119,6 @@ export const constructAuthOptions = ( const data = await signInSchema.parseAsync(credentials); const user = await prisma.user.findFirst({ - include: { - roles: true, - }, where: { name: data.name, }, @@ -146,6 +145,7 @@ export const constructAuthOptions = ( }; }, }), + EmptyNextAuthProvider(), ], jwt: { async encode(params) { diff --git a/src/utils/empty-provider.ts b/src/utils/empty-provider.ts new file mode 100644 index 000000000..692d3c906 --- /dev/null +++ b/src/utils/empty-provider.ts @@ -0,0 +1,14 @@ +import { OAuthConfig } from 'next-auth/providers'; + +export default function EmptyNextAuthProvider(): OAuthConfig { + return { + id: 'empty', + name: 'Empty', + type: 'oauth', + profile: () => { + throw new Error( + 'EmptyNextAuthProvider can not be used and is only a placeholder because credentials authentication can not be used as session authentication without additional providers.' + ); + }, + }; +} diff --git a/yarn.lock b/yarn.lock index 636cd5b95..c8ae9949d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -38,33 +38,33 @@ __metadata: languageName: node linkType: hard -"@babel/compat-data@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/compat-data@npm:7.22.5" - checksum: eb1a47ebf79ae268b4a16903e977be52629339806e248455eb9973897c503a04b701f36a9de64e19750d6e081d0561e77a514c8dc470babbeba59ae94298ed18 +"@babel/compat-data@npm:^7.22.9": + version: 7.22.9 + resolution: "@babel/compat-data@npm:7.22.9" + checksum: bed77d9044ce948b4327b30dd0de0779fa9f3a7ed1f2d31638714ed00229fa71fc4d1617ae0eb1fad419338d3658d0e9a5a083297451e09e73e078d0347ff808 languageName: node linkType: hard "@babel/core@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/core@npm:7.22.5" + version: 7.22.9 + resolution: "@babel/core@npm:7.22.9" dependencies: "@ampproject/remapping": ^2.2.0 "@babel/code-frame": ^7.22.5 - "@babel/generator": ^7.22.5 - "@babel/helper-compilation-targets": ^7.22.5 - "@babel/helper-module-transforms": ^7.22.5 - "@babel/helpers": ^7.22.5 - "@babel/parser": ^7.22.5 + "@babel/generator": ^7.22.9 + "@babel/helper-compilation-targets": ^7.22.9 + "@babel/helper-module-transforms": ^7.22.9 + "@babel/helpers": ^7.22.6 + "@babel/parser": ^7.22.7 "@babel/template": ^7.22.5 - "@babel/traverse": ^7.22.5 + "@babel/traverse": ^7.22.8 "@babel/types": ^7.22.5 convert-source-map: ^1.7.0 debug: ^4.1.0 gensync: ^1.0.0-beta.2 json5: ^2.2.2 - semver: ^6.3.0 - checksum: 173ae426958c90c7bbd7de622c6f13fcab8aef0fac3f138e2d47bc466d1cd1f86f71ca82ae0acb9032fd8794abed8efb56fea55c031396337eaec0d673b69d56 + semver: ^6.3.1 + checksum: 7bf069aeceb417902c4efdaefab1f7b94adb7dea694a9aed1bda2edf4135348a080820529b1a300c6f8605740a00ca00c19b2d5e74b5dd489d99d8c11d5e56d1 languageName: node linkType: hard @@ -79,30 +79,30 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:^7.17.3, @babel/generator@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/generator@npm:7.22.5" +"@babel/generator@npm:^7.17.3, @babel/generator@npm:^7.22.7, @babel/generator@npm:^7.22.9": + version: 7.22.9 + resolution: "@babel/generator@npm:7.22.9" dependencies: "@babel/types": ^7.22.5 "@jridgewell/gen-mapping": ^0.3.2 "@jridgewell/trace-mapping": ^0.3.17 jsesc: ^2.5.1 - checksum: efa64da70ca88fe69f05520cf5feed6eba6d30a85d32237671488cc355fdc379fe2c3246382a861d49574c4c2f82a317584f8811e95eb024e365faff3232b49d + checksum: 7c9d2c58b8d5ac5e047421a6ab03ec2ff5d9a5ff2c2212130a0055e063ac349e0b19d435537d6886c999771aef394832e4f54cd9fc810100a7f23d982f6af06b languageName: node linkType: hard -"@babel/helper-compilation-targets@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/helper-compilation-targets@npm:7.22.5" +"@babel/helper-compilation-targets@npm:^7.22.9": + version: 7.22.9 + resolution: "@babel/helper-compilation-targets@npm:7.22.9" dependencies: - "@babel/compat-data": ^7.22.5 + "@babel/compat-data": ^7.22.9 "@babel/helper-validator-option": ^7.22.5 - browserslist: ^4.21.3 + browserslist: ^4.21.9 lru-cache: ^5.1.1 - semver: ^6.3.0 + semver: ^6.3.1 peerDependencies: "@babel/core": ^7.0.0 - checksum: a479460615acffa0f4fd0a29b740eafb53a93694265207d23a6038ccd18d183a382cacca515e77b7c9b042c3ba80b0aca0da5f1f62215140e81660d2cf721b68 + checksum: ea0006c6a93759025f4a35a25228ae260538c9f15023e8aac2a6d45ca68aef4cf86cfc429b19af9a402cbdd54d5de74ad3fbcf6baa7e48184dc079f1a791e178 languageName: node linkType: hard @@ -141,19 +141,18 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-transforms@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/helper-module-transforms@npm:7.22.5" +"@babel/helper-module-transforms@npm:^7.22.9": + version: 7.22.9 + resolution: "@babel/helper-module-transforms@npm:7.22.9" dependencies: "@babel/helper-environment-visitor": ^7.22.5 "@babel/helper-module-imports": ^7.22.5 "@babel/helper-simple-access": ^7.22.5 - "@babel/helper-split-export-declaration": ^7.22.5 + "@babel/helper-split-export-declaration": ^7.22.6 "@babel/helper-validator-identifier": ^7.22.5 - "@babel/template": ^7.22.5 - "@babel/traverse": ^7.22.5 - "@babel/types": ^7.22.5 - checksum: 8985dc0d971fd17c467e8b84fe0f50f3dd8610e33b6c86e5b3ca8e8859f9448bcc5c84e08a2a14285ef388351c0484797081c8f05a03770bf44fc27bf4900e68 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 2751f77660518cf4ff027514d6f4794f04598c6393be7b04b8e46c6e21606e11c19f3f57ab6129a9c21bacdf8b3ffe3af87bb401d972f34af2d0ffde02ac3001 languageName: node linkType: hard @@ -173,12 +172,12 @@ __metadata: languageName: node linkType: hard -"@babel/helper-split-export-declaration@npm:^7.16.7, @babel/helper-split-export-declaration@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/helper-split-export-declaration@npm:7.22.5" +"@babel/helper-split-export-declaration@npm:^7.16.7, @babel/helper-split-export-declaration@npm:^7.22.6": + version: 7.22.6 + resolution: "@babel/helper-split-export-declaration@npm:7.22.6" dependencies: "@babel/types": ^7.22.5 - checksum: d10e05a02f49c1f7c578cea63d2ac55356501bbf58856d97ac9bfde4957faee21ae97c7f566aa309e38a256eef58b58e5b670a7f568b362c00e93dfffe072650 + checksum: e141cace583b19d9195f9c2b8e17a3ae913b7ee9b8120246d0f9ca349ca6f03cb2c001fd5ec57488c544347c0bb584afec66c936511e447fd20a360e591ac921 languageName: node linkType: hard @@ -203,14 +202,14 @@ __metadata: languageName: node linkType: hard -"@babel/helpers@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/helpers@npm:7.22.5" +"@babel/helpers@npm:^7.22.6": + version: 7.22.6 + resolution: "@babel/helpers@npm:7.22.6" dependencies: "@babel/template": ^7.22.5 - "@babel/traverse": ^7.22.5 + "@babel/traverse": ^7.22.6 "@babel/types": ^7.22.5 - checksum: a96e785029dff72f171190943df895ab0f76e17bf3881efd630bc5fae91215042d1c2e9ed730e8e4adf4da6f28b24bd1f54ed93b90ffbca34c197351872a084e + checksum: 5c1f33241fe7bf7709868c2105134a0a86dca26a0fbd508af10a89312b1f77ca38ebae43e50be3b208613c5eacca1559618af4ca236f0abc55d294800faeff30 languageName: node linkType: hard @@ -225,12 +224,12 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.17.3, @babel/parser@npm:^7.20.5, @babel/parser@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/parser@npm:7.22.5" +"@babel/parser@npm:^7.17.3, @babel/parser@npm:^7.20.5, @babel/parser@npm:^7.22.5, @babel/parser@npm:^7.22.7": + version: 7.22.7 + resolution: "@babel/parser@npm:7.22.7" bin: parser: ./bin/babel-parser.js - checksum: 470ebba516417ce8683b36e2eddd56dcfecb32c54b9bb507e28eb76b30d1c3e618fd0cfeee1f64d8357c2254514e1a19e32885cfb4e73149f4ae875436a6d59c + checksum: 02209ddbd445831ee8bf966fdf7c29d189ed4b14343a68eb2479d940e7e3846340d7cc6bd654a5f3d87d19dc84f49f50a58cf9363bee249dc5409ff3ba3dab54 languageName: node linkType: hard @@ -257,11 +256,11 @@ __metadata: linkType: hard "@babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.20.6, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2": - version: 7.22.5 - resolution: "@babel/runtime@npm:7.22.5" + version: 7.22.6 + resolution: "@babel/runtime@npm:7.22.6" dependencies: regenerator-runtime: ^0.13.11 - checksum: 12a50b7de2531beef38840d17af50c55a094253697600cee255311222390c68eed704829308d4fd305e1b3dfbce113272e428e9d9d45b1730e0fede997eaceb1 + checksum: e585338287c4514a713babf4fdb8fc2a67adcebab3e7723a739fc62c79cfda875b314c90fd25f827afb150d781af97bc16c85bfdbfa2889f06053879a1ddb597 languageName: node linkType: hard @@ -294,21 +293,21 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/traverse@npm:7.22.5" +"@babel/traverse@npm:^7.22.6, @babel/traverse@npm:^7.22.8": + version: 7.22.8 + resolution: "@babel/traverse@npm:7.22.8" dependencies: "@babel/code-frame": ^7.22.5 - "@babel/generator": ^7.22.5 + "@babel/generator": ^7.22.7 "@babel/helper-environment-visitor": ^7.22.5 "@babel/helper-function-name": ^7.22.5 "@babel/helper-hoist-variables": ^7.22.5 - "@babel/helper-split-export-declaration": ^7.22.5 - "@babel/parser": ^7.22.5 + "@babel/helper-split-export-declaration": ^7.22.6 + "@babel/parser": ^7.22.7 "@babel/types": ^7.22.5 debug: ^4.1.0 globals: ^11.1.0 - checksum: 560931422dc1761f2df723778dcb4e51ce0d02e560cf2caa49822921578f49189a5a7d053b78a32dca33e59be886a6b2200a6e24d4ae9b5086ca0ba803815694 + checksum: a381369bc3eedfd13ed5fef7b884657f1c29024ea7388198149f0edc34bd69ce3966e9f40188d15f56490a5e12ba250ccc485f2882b53d41b054fccefb233e33 languageName: node linkType: hard @@ -588,161 +587,161 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm64@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/android-arm64@npm:0.17.19" +"@esbuild/android-arm64@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/android-arm64@npm:0.18.17" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"@esbuild/android-arm@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/android-arm@npm:0.17.19" +"@esbuild/android-arm@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/android-arm@npm:0.18.17" conditions: os=android & cpu=arm languageName: node linkType: hard -"@esbuild/android-x64@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/android-x64@npm:0.17.19" +"@esbuild/android-x64@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/android-x64@npm:0.18.17" conditions: os=android & cpu=x64 languageName: node linkType: hard -"@esbuild/darwin-arm64@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/darwin-arm64@npm:0.17.19" +"@esbuild/darwin-arm64@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/darwin-arm64@npm:0.18.17" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@esbuild/darwin-x64@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/darwin-x64@npm:0.17.19" +"@esbuild/darwin-x64@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/darwin-x64@npm:0.18.17" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@esbuild/freebsd-arm64@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/freebsd-arm64@npm:0.17.19" +"@esbuild/freebsd-arm64@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/freebsd-arm64@npm:0.18.17" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard -"@esbuild/freebsd-x64@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/freebsd-x64@npm:0.17.19" +"@esbuild/freebsd-x64@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/freebsd-x64@npm:0.18.17" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"@esbuild/linux-arm64@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/linux-arm64@npm:0.17.19" +"@esbuild/linux-arm64@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/linux-arm64@npm:0.18.17" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"@esbuild/linux-arm@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/linux-arm@npm:0.17.19" +"@esbuild/linux-arm@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/linux-arm@npm:0.18.17" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@esbuild/linux-ia32@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/linux-ia32@npm:0.17.19" +"@esbuild/linux-ia32@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/linux-ia32@npm:0.18.17" conditions: os=linux & cpu=ia32 languageName: node linkType: hard -"@esbuild/linux-loong64@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/linux-loong64@npm:0.17.19" +"@esbuild/linux-loong64@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/linux-loong64@npm:0.18.17" conditions: os=linux & cpu=loong64 languageName: node linkType: hard -"@esbuild/linux-mips64el@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/linux-mips64el@npm:0.17.19" +"@esbuild/linux-mips64el@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/linux-mips64el@npm:0.18.17" conditions: os=linux & cpu=mips64el languageName: node linkType: hard -"@esbuild/linux-ppc64@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/linux-ppc64@npm:0.17.19" +"@esbuild/linux-ppc64@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/linux-ppc64@npm:0.18.17" conditions: os=linux & cpu=ppc64 languageName: node linkType: hard -"@esbuild/linux-riscv64@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/linux-riscv64@npm:0.17.19" +"@esbuild/linux-riscv64@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/linux-riscv64@npm:0.18.17" conditions: os=linux & cpu=riscv64 languageName: node linkType: hard -"@esbuild/linux-s390x@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/linux-s390x@npm:0.17.19" +"@esbuild/linux-s390x@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/linux-s390x@npm:0.18.17" conditions: os=linux & cpu=s390x languageName: node linkType: hard -"@esbuild/linux-x64@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/linux-x64@npm:0.17.19" +"@esbuild/linux-x64@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/linux-x64@npm:0.18.17" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"@esbuild/netbsd-x64@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/netbsd-x64@npm:0.17.19" +"@esbuild/netbsd-x64@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/netbsd-x64@npm:0.18.17" conditions: os=netbsd & cpu=x64 languageName: node linkType: hard -"@esbuild/openbsd-x64@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/openbsd-x64@npm:0.17.19" +"@esbuild/openbsd-x64@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/openbsd-x64@npm:0.18.17" conditions: os=openbsd & cpu=x64 languageName: node linkType: hard -"@esbuild/sunos-x64@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/sunos-x64@npm:0.17.19" +"@esbuild/sunos-x64@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/sunos-x64@npm:0.18.17" conditions: os=sunos & cpu=x64 languageName: node linkType: hard -"@esbuild/win32-arm64@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/win32-arm64@npm:0.17.19" +"@esbuild/win32-arm64@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/win32-arm64@npm:0.18.17" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@esbuild/win32-ia32@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/win32-ia32@npm:0.17.19" +"@esbuild/win32-ia32@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/win32-ia32@npm:0.18.17" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@esbuild/win32-x64@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/win32-x64@npm:0.17.19" +"@esbuild/win32-x64@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/win32-x64@npm:0.18.17" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.3.0": +"@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0": version: 4.4.0 resolution: "@eslint-community/eslint-utils@npm:4.4.0" dependencies: @@ -753,16 +752,16 @@ __metadata: languageName: node linkType: hard -"@eslint-community/regexpp@npm:^4.4.0, @eslint-community/regexpp@npm:^4.5.0": - version: 4.5.1 - resolution: "@eslint-community/regexpp@npm:4.5.1" - checksum: 6d901166d64998d591fab4db1c2f872981ccd5f6fe066a1ad0a93d4e11855ecae6bfb76660869a469563e8882d4307228cebd41142adb409d182f2966771e57e +"@eslint-community/regexpp@npm:^4.5.1, @eslint-community/regexpp@npm:^4.6.1": + version: 4.6.2 + resolution: "@eslint-community/regexpp@npm:4.6.2" + checksum: a3c341377b46b54fa228f455771b901d1a2717f95d47dcdf40199df30abc000ba020f747f114f08560d119e979d882a94cf46cfc51744544d54b00319c0f2724 languageName: node linkType: hard -"@eslint/eslintrc@npm:^2.1.0": - version: 2.1.0 - resolution: "@eslint/eslintrc@npm:2.1.0" +"@eslint/eslintrc@npm:^2.1.1": + version: 2.1.1 + resolution: "@eslint/eslintrc@npm:2.1.1" dependencies: ajv: ^6.12.4 debug: ^4.3.2 @@ -773,30 +772,33 @@ __metadata: js-yaml: ^4.1.0 minimatch: ^3.1.2 strip-json-comments: ^3.1.1 - checksum: d5ed0adbe23f6571d8c9bb0ca6edf7618dc6aed4046aa56df7139f65ae7b578874e0d9c796df784c25bda648ceb754b6320277d828c8b004876d7443b8dc018c + checksum: bf909ea183d27238c257a82d4ffdec38ca94b906b4b8dfae02ecbe7ecc9e5a8182ef5e469c808bb8cb4fea4750f43ac4ca7c4b4a167b6cd7e3aaacd386b2bd25 languageName: node linkType: hard -"@eslint/js@npm:8.44.0": - version: 8.44.0 - resolution: "@eslint/js@npm:8.44.0" - checksum: fc539583226a28f5677356e9f00d2789c34253f076643d2e32888250e509a4e13aafe0880cb2425139051de0f3a48d25bfc5afa96b7304f203b706c17340e3cf +"@eslint/js@npm:^8.46.0": + version: 8.46.0 + resolution: "@eslint/js@npm:8.46.0" + checksum: 7aed479832302882faf5bec37e9d068f270f84c19b3fb529646a7c1b031e73a312f730569c78806492bc09cfce3d7651dfab4ce09a56cbb06bc6469449e56377 languageName: node linkType: hard -"@floating-ui/core@npm:^1.2.6": - version: 1.2.6 - resolution: "@floating-ui/core@npm:1.2.6" - checksum: e4aa96c435277f1720d4bc939e17a79b1e1eebd589c20b622d3c646a5273590ff889b8c6e126f7be61873cf8c4d7db7d418895986ea19b8b0d0530de32504c3a +"@floating-ui/core@npm:^1.4.0": + version: 1.4.0 + resolution: "@floating-ui/core@npm:1.4.0" + dependencies: + "@floating-ui/utils": ^0.1.0 + checksum: 8549ad349dab33498d79a3fb558616eb80c2d0301ee7739832e49d51398da6e7fa2b2e4f4088af2c346c011ce2c4a61a2d1348838c6e0119e15e81a827ba00a7 languageName: node linkType: hard "@floating-ui/dom@npm:^1.2.1": - version: 1.2.9 - resolution: "@floating-ui/dom@npm:1.2.9" + version: 1.5.0 + resolution: "@floating-ui/dom@npm:1.5.0" dependencies: - "@floating-ui/core": ^1.2.6 - checksum: 16ae5e05a41c2ca16d51579d12729ca9d346241319f68ce5678f5fbeb9c4f9a16176c95089bbd7a0eb37c6ed90e5fd55a310ffc9948af7c841d5b8bfa0afe1b8 + "@floating-ui/core": ^1.4.0 + "@floating-ui/utils": ^0.1.0 + checksum: 61e97e0625279deb2e9fba9d7f85491ce3bfdbaf0adc31f2ee101a82e2879dd62945ee9ea0c83cf82481a711af9a43ccfd31b9ec017718be8b9028ff2a1da5b2 languageName: node linkType: hard @@ -826,10 +828,10 @@ __metadata: languageName: node linkType: hard -"@gar/promisify@npm:^1.1.3": - version: 1.1.3 - resolution: "@gar/promisify@npm:1.1.3" - checksum: 4059f790e2d07bf3c3ff3e0fec0daa8144fe35c1f6e0111c9921bd32106adaa97a4ab096ad7dab1e28ee6a9060083c4d1a4ada42a7f5f3f7a96b8812e2b757c1 +"@floating-ui/utils@npm:^0.1.0": + version: 0.1.0 + resolution: "@floating-ui/utils@npm:0.1.0" + checksum: 71c865f0c6ab3f0a66355b80ca1b6438ea4bbf3aaa66421fc9bbddea08e814b94a75b25cb9c0cba09fd8775b8952b8e59affecf5db4dc3235849e25b2c011dbe languageName: node linkType: hard @@ -858,6 +860,20 @@ __metadata: languageName: node linkType: hard +"@isaacs/cliui@npm:^8.0.2": + version: 8.0.2 + resolution: "@isaacs/cliui@npm:8.0.2" + dependencies: + string-width: ^5.1.2 + string-width-cjs: "npm:string-width@^4.2.0" + strip-ansi: ^7.0.1 + strip-ansi-cjs: "npm:strip-ansi@^6.0.1" + wrap-ansi: ^8.1.0 + wrap-ansi-cjs: "npm:wrap-ansi@^7.0.0" + checksum: 4a473b9b32a7d4d3cfb7a614226e555091ff0c5a29a1734c28c72a182c2f6699b26fc6b5c2131dfd841e86b185aea714c72201d7c98c2fba5f17709333a67aeb + languageName: node + linkType: hard + "@istanbuljs/schema@npm:^0.1.2, @istanbuljs/schema@npm:^0.1.3": version: 0.1.3 resolution: "@istanbuljs/schema@npm:0.1.3" @@ -876,35 +892,35 @@ __metadata: languageName: node linkType: hard -"@jest/expect-utils@npm:^29.5.0": - version: 29.5.0 - resolution: "@jest/expect-utils@npm:29.5.0" +"@jest/expect-utils@npm:^29.6.2": + version: 29.6.2 + resolution: "@jest/expect-utils@npm:29.6.2" dependencies: jest-get-type: ^29.4.3 - checksum: c46fb677c88535cf83cf29f0a5b1f376c6a1109ddda266ad7da1a9cbc53cb441fa402dd61fc7b111ffc99603c11a9b3357ee41a1c0e035a58830bcb360871476 + checksum: 0decf2009aa3735f9df469e78ce1721c2815e4278439887e0cf0321ca8979541a22515d114a59b2445a6cd70a074b09dc9c00b5e7b3b3feac5174b9c4a78b2e1 languageName: node linkType: hard -"@jest/schemas@npm:^29.4.3": - version: 29.4.3 - resolution: "@jest/schemas@npm:29.4.3" +"@jest/schemas@npm:^29.6.0": + version: 29.6.0 + resolution: "@jest/schemas@npm:29.6.0" dependencies: - "@sinclair/typebox": ^0.25.16 - checksum: ac754e245c19dc39e10ebd41dce09040214c96a4cd8efa143b82148e383e45128f24599195ab4f01433adae4ccfbe2db6574c90db2862ccd8551a86704b5bebd + "@sinclair/typebox": ^0.27.8 + checksum: c00511c69cf89138a7d974404d3a5060af375b5a52b9c87215d91873129b382ca11c1ff25bd6d605951404bb381ddce5f8091004a61e76457da35db1f5c51365 languageName: node linkType: hard -"@jest/types@npm:^29.5.0": - version: 29.5.0 - resolution: "@jest/types@npm:29.5.0" +"@jest/types@npm:^29.6.1": + version: 29.6.1 + resolution: "@jest/types@npm:29.6.1" dependencies: - "@jest/schemas": ^29.4.3 + "@jest/schemas": ^29.6.0 "@types/istanbul-lib-coverage": ^2.0.0 "@types/istanbul-reports": ^3.0.0 "@types/node": "*" "@types/yargs": ^17.0.8 chalk: ^4.0.0 - checksum: 1811f94b19cf8a9460a289c4f056796cfc373480e0492692a6125a553cd1a63824bd846d7bb78820b7b6f758f6dd3c2d4558293bb676d541b2fa59c70fdf9d39 + checksum: 89fc1ccf71a84fe0da643e0675b1cfe6a6f19ea72e935b2ab1dbdb56ec547e94433fb59b3536d3832a6e156c077865b7176fe9dae707dab9c3d2f9405ba6233c languageName: node linkType: hard @@ -1142,7 +1158,7 @@ __metadata: languageName: node linkType: hard -"@next-auth/prisma-adapter@npm:^1.0.7": +"@next-auth/prisma-adapter@npm:^1.0.5": version: 1.0.7 resolution: "@next-auth/prisma-adapter@npm:1.0.7" peerDependencies: @@ -1153,11 +1169,11 @@ __metadata: linkType: hard "@next/bundle-analyzer@npm:^13.0.0": - version: 13.4.10 - resolution: "@next/bundle-analyzer@npm:13.4.10" + version: 13.4.12 + resolution: "@next/bundle-analyzer@npm:13.4.12" dependencies: webpack-bundle-analyzer: 4.7.0 - checksum: 876474d910583c8326c6ae1c23124461783c8c58abb1c9f197185f96c36b71d0d19861cd54ebaa0db5317f6fd035d465a703a3c70a2006492c0e2aa25621fceb + checksum: 4fdd226df19e830537f460d019af9d17e2e2c70b516100f1ab31d86de21b7753630f7ccc7d37d44dd6abdc4823ed4a502e10747140d5de7a40e9e7832cf5fcc0 languageName: node linkType: hard @@ -1168,12 +1184,12 @@ __metadata: languageName: node linkType: hard -"@next/eslint-plugin-next@npm:13.4.10, @next/eslint-plugin-next@npm:^13.4.5": - version: 13.4.10 - resolution: "@next/eslint-plugin-next@npm:13.4.10" +"@next/eslint-plugin-next@npm:13.4.12, @next/eslint-plugin-next@npm:^13.4.5": + version: 13.4.12 + resolution: "@next/eslint-plugin-next@npm:13.4.12" dependencies: glob: 7.1.7 - checksum: f14b99eb5d33b6ede9666ffafb596ee6be52157fc87b59d10d94e44b1e9836099ad450a67558c2aecf09c84b55f65a33c9254ab72df33f55f7cc9f4abee7b38c + checksum: a20cf430efe7602b819e69d826c33659a9c2cfa7c6162f63d91b607ff6ac9da0e79ff27f38cebd79117500640329bd107521b4874e6a2d009ddafab762885c82 languageName: node linkType: hard @@ -1436,23 +1452,12 @@ __metadata: languageName: node linkType: hard -"@npmcli/fs@npm:^2.1.0": - version: 2.1.2 - resolution: "@npmcli/fs@npm:2.1.2" +"@npmcli/fs@npm:^3.1.0": + version: 3.1.0 + resolution: "@npmcli/fs@npm:3.1.0" dependencies: - "@gar/promisify": ^1.1.3 semver: ^7.3.5 - checksum: 405074965e72d4c9d728931b64d2d38e6ea12066d4fad651ac253d175e413c06fe4350970c783db0d749181da8fe49c42d3880bd1cbc12cd68e3a7964d820225 - languageName: node - linkType: hard - -"@npmcli/move-file@npm:^2.0.0": - version: 2.0.1 - resolution: "@npmcli/move-file@npm:2.0.1" - dependencies: - mkdirp: ^1.0.4 - rimraf: ^3.0.2 - checksum: 52dc02259d98da517fae4cb3a0a3850227bdae4939dda1980b788a7670636ca2b4a01b58df03dd5f65c1e3cb70c50fa8ce5762b582b3f499ec30ee5ce1fd9380 + checksum: a50a6818de5fc557d0b0e6f50ec780a7a02ab8ad07e5ac8b16bf519e0ad60a144ac64f97d05c443c3367235d337182e1d012bbac0eb8dbae8dc7b40b193efd0e languageName: node linkType: hard @@ -1463,17 +1468,24 @@ __metadata: languageName: node linkType: hard +"@pkgjs/parseargs@npm:^0.11.0": + version: 0.11.0 + resolution: "@pkgjs/parseargs@npm:0.11.0" + checksum: 6ad6a00fc4f2f2cfc6bff76fb1d88b8ee20bc0601e18ebb01b6d4be583733a860239a521a7fbca73b612e66705078809483549d2b18f370eb346c5155c8e4a0f + languageName: node + linkType: hard + "@pkgr/utils@npm:^2.3.1": - version: 2.4.1 - resolution: "@pkgr/utils@npm:2.4.1" + version: 2.4.2 + resolution: "@pkgr/utils@npm:2.4.2" dependencies: cross-spawn: ^7.0.3 - fast-glob: ^3.2.12 + fast-glob: ^3.3.0 is-glob: ^4.0.3 open: ^9.1.0 picocolors: ^1.0.0 - tslib: ^2.5.0 - checksum: 654682860272541a40485b01e0763b155ec31faeba85b2c51e38b59c4ff1f8918c37b87b5ecbda3ff482d8486eba086e92b991fe4a8ed62efbbbdf83c0f64409 + tslib: ^2.6.0 + checksum: 24e04c121269317d259614cd32beea3af38277151c4002df5883c4be920b8e3490bb897748e844f9d46bf68230f86dabd4e8f093773130e7e60529a769a132fc languageName: node linkType: hard @@ -1646,94 +1658,85 @@ __metadata: linkType: hard "@react-native-async-storage/async-storage@npm:^1.18.1": - version: 1.19.0 - resolution: "@react-native-async-storage/async-storage@npm:1.19.0" + version: 1.19.1 + resolution: "@react-native-async-storage/async-storage@npm:1.19.1" dependencies: merge-options: ^3.0.4 peerDependencies: react-native: ^0.0.0-0 || 0.60 - 0.72 || 1000.0.0 - checksum: 669f50695838a010f6444f0c0e0618aeda072d25798901d0802f0b945aa6fbab13a68d83a23f6bef591793ae09341dbb8a88f2d40eaf9542bde159711d9deb0e + checksum: 7367210e16f788999ca8ff96bd04bbd345f44c186cec7c50903d55637f572c73b8a79f9c948a549329ad489c08d77dd49367971691ed54dbc3839285e0194431 languageName: node linkType: hard -"@react-spring/animated@npm:~9.7.2": - version: 9.7.2 - resolution: "@react-spring/animated@npm:9.7.2" +"@react-spring/animated@npm:~9.7.3": + version: 9.7.3 + resolution: "@react-spring/animated@npm:9.7.3" dependencies: - "@react-spring/shared": ~9.7.2 - "@react-spring/types": ~9.7.2 + "@react-spring/shared": ~9.7.3 + "@react-spring/types": ~9.7.3 peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: 08f2f1e54468776b2abf76e3c3504129b8d965875781bc1cfd40adddff8f310869ecfb6d18e937df8ed63a01b3d0cb2335fb531be03ddf25f6f58583a91e08e7 + checksum: 468942ca3a11c02c3e56def26b2da9dd10ddbed548004245c4ac309cce00b58d971e781abed67db0d652f72737eaa73766ea9a43b8ef3b08a7ed2eddc04d4c39 languageName: node linkType: hard -"@react-spring/core@npm:~9.7.2": - version: 9.7.2 - resolution: "@react-spring/core@npm:9.7.2" +"@react-spring/core@npm:~9.7.3": + version: 9.7.3 + resolution: "@react-spring/core@npm:9.7.3" dependencies: - "@react-spring/animated": ~9.7.2 - "@react-spring/rafz": ~9.7.2 - "@react-spring/shared": ~9.7.2 - "@react-spring/types": ~9.7.2 + "@react-spring/animated": ~9.7.3 + "@react-spring/shared": ~9.7.3 + "@react-spring/types": ~9.7.3 peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: 7d14369d6bc3cb4f51bce946a0499484bd03a06b2602edd1f0478806759fd2a5f0800892cdf8d38a3bd4ef949f3c61de65933c46784683ea757f16db710a04fd + checksum: 8a80a07276458fd14099320eda824e58a11ce3a9b03a5c9cd3f4252adb4d26da04ee5caf5cbc961199f55c2d58a99638d5ea292cdb6aa029208dbab741b5c531 languageName: node linkType: hard -"@react-spring/rafz@npm:~9.7.2": - version: 9.7.2 - resolution: "@react-spring/rafz@npm:9.7.2" - checksum: 88ad6275ed172745c7cd309e6a06bb52b76b6f391510afcf8ad12d1fc50950e74f902f96bcaae0895ab75e47a1d809d36fe843ce8d3d0d2ea2d6700ff9e03d81 - languageName: node - linkType: hard - -"@react-spring/shared@npm:~9.7.2": - version: 9.7.2 - resolution: "@react-spring/shared@npm:9.7.2" +"@react-spring/shared@npm:~9.7.3": + version: 9.7.3 + resolution: "@react-spring/shared@npm:9.7.3" dependencies: - "@react-spring/rafz": ~9.7.2 - "@react-spring/types": ~9.7.2 + "@react-spring/types": ~9.7.3 peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: f3041a537dddad63c64e68890f3276b72c4e2d5828621a09dbd722edb7a12edc0026555118f8305b969253f44e409ddd411dd5b99ec4e04edea32bee5e5f57fd + checksum: 912b5e567eb5345c9a6c8e8c0c2d69b1f411af72a0685b95831809c267c89846a31341ca071f284ace98b3cb5de647054dc76f6ace81d6379513eaf96b52f195 languageName: node linkType: hard -"@react-spring/types@npm:~9.7.2": - version: 9.7.2 - resolution: "@react-spring/types@npm:9.7.2" - checksum: 145a79d2f40dfc9f0a4b54db17890c6471b4e50563c01951ca13022977b0d8009865c1839cf07a762fd33ae0b83f2d6972e2d0fe50bd3105942b9cea5253612e +"@react-spring/types@npm:~9.7.3": + version: 9.7.3 + resolution: "@react-spring/types@npm:9.7.3" + checksum: f47b81fe556464aa54a78603311cb584d6a0f03088522229afb058265bbe2ade2095a55ec7f4e960c3b9cceaa5d47865bc41fc6643c0f5f4bd3d8650203d8389 languageName: node linkType: hard "@react-spring/web@npm:9.4.5 || ^9.7.2": - version: 9.7.2 - resolution: "@react-spring/web@npm:9.7.2" + version: 9.7.3 + resolution: "@react-spring/web@npm:9.7.3" dependencies: - "@react-spring/animated": ~9.7.2 - "@react-spring/core": ~9.7.2 - "@react-spring/shared": ~9.7.2 - "@react-spring/types": ~9.7.2 + "@react-spring/animated": ~9.7.3 + "@react-spring/core": ~9.7.3 + "@react-spring/shared": ~9.7.3 + "@react-spring/types": ~9.7.3 peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: f872dfd9bbbfc27f32e4406076729a36169e2da3785709fbf981b7d3a77a1a430d7f295b300a9acc8cb4805d0965e6d9a54914a4263abfd96806b7ae53ba69cd + checksum: 7f5cd05b2314b7f2f715e1926abcf9aa0a539399b222ab34e989144f48350adfcd2edab65d41425570f72c57f602fc6994d6730fbeed902171ac527b630a8a9b languageName: node linkType: hard "@rushstack/eslint-patch@npm:^1.1.3": - version: 1.3.1 - resolution: "@rushstack/eslint-patch@npm:1.3.1" - checksum: 951023fffbfa71696eab8d7e6676f3bea0b8efc954e06c4ab10cacd94d1323744ad94c2ed8e81b0c9390df4a8cefbed1faacd2bf56e4c26f2b41852719273e24 + version: 1.3.2 + resolution: "@rushstack/eslint-patch@npm:1.3.2" + checksum: 010c87ef2d901faaaf70ea1bf86fd3e7b74f24e23205f836e9a32790bca2076afe5de58ded03c35cb482f83691c8d22b1a0c34291b075bfe81afd26cfa5d14cc languageName: node linkType: hard -"@sinclair/typebox@npm:^0.25.16": - version: 0.25.24 - resolution: "@sinclair/typebox@npm:0.25.24" - checksum: 10219c58f40b8414c50b483b0550445e9710d4fe7b2c4dccb9b66533dd90ba8e024acc776026cebe81e87f06fa24b07fdd7bc30dd277eb9cc386ec50151a3026 +"@sinclair/typebox@npm:^0.27.8": + version: 0.27.8 + resolution: "@sinclair/typebox@npm:0.27.8" + checksum: 00bd7362a3439021aa1ea51b0e0d0a0e8ca1351a3d54c606b115fdcc49b51b16db6e5f43b4fe7a28c38688523e22a94d49dd31168868b655f0d4d50f032d07a1 languageName: node linkType: hard @@ -1745,9 +1748,9 @@ __metadata: linkType: hard "@sindresorhus/is@npm:^5.2.0": - version: 5.4.1 - resolution: "@sindresorhus/is@npm:5.4.1" - checksum: 178386d27f077dd88885263da2e77f826a3d2c476293a142a994aa876ee7d9b0d1672e5f32a12790e92a034462e17a4d0c743e6b915e0f1bb4e3471b3b17967e + version: 5.6.0 + resolution: "@sindresorhus/is@npm:5.6.0" + checksum: 2e6e0c3acf188dcd9aea0f324ac1b6ad04c9fc672392a7b5a1218512fcde066965797eba8b9fe2108657a504388bd4a6664e6e6602555168e828a6df08b9f10e languageName: node linkType: hard @@ -1801,21 +1804,21 @@ __metadata: linkType: hard "@tabler/icons-react@npm:^2.18.0": - version: 2.26.0 - resolution: "@tabler/icons-react@npm:2.26.0" + version: 2.30.0 + resolution: "@tabler/icons-react@npm:2.30.0" dependencies: - "@tabler/icons": 2.26.0 + "@tabler/icons": 2.30.0 prop-types: ^15.7.2 peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 - checksum: 7f91263fd01a25490a3fcbafcf61a510b5d6a646e76e6eacc4a37f4d7ed119377bf245aa2d1e1e9e736ef51f4a4ca110677078bf76a0809bc07a88ff120ac0e4 + checksum: 2d137865d24b2034820e5edcb99d938a899b29d31613671d9b574823a614f460d90cdd4efd39c1571b7244225e339a722daddc58921569079b4ed530162354b6 languageName: node linkType: hard -"@tabler/icons@npm:2.26.0": - version: 2.26.0 - resolution: "@tabler/icons@npm:2.26.0" - checksum: 6d4707dc34fa25a408aa4e01a4c340c68ed2109ae204fddb72e1b87cb979680e825f3e6520df036ec8f7672afab662e6e8e08ecde75cdbda9c3ff53e16127a3d +"@tabler/icons@npm:2.30.0": + version: 2.30.0 + resolution: "@tabler/icons@npm:2.30.0" + checksum: d89255ea6bb0a5280f9d23ab85005ef0f7c7c0198904db7d7d6cbc0289c7ba108c433c6f9fb6531fc127619ed61452c6c5832e7c7cc49a5889faad412636e618 languageName: node linkType: hard @@ -1829,70 +1832,70 @@ __metadata: linkType: hard "@tanstack/query-async-storage-persister@npm:^4.27.1": - version: 4.29.25 - resolution: "@tanstack/query-async-storage-persister@npm:4.29.25" + version: 4.32.0 + resolution: "@tanstack/query-async-storage-persister@npm:4.32.0" dependencies: - "@tanstack/query-persist-client-core": 4.29.25 - checksum: 27a35caf8562e37b58921567176ed9a21864b47b0763588bdd18ea44243b021ce649449f0ad4d8be51852cae90b6668acfb20f7098f83e062d452de92fd1a037 + "@tanstack/query-persist-client-core": 4.32.0 + checksum: 21151138fed9620c5752e226d368cfe09cb928102d664f2c2e423ab7ba4c2f61ea9b56b91dfdf1fb50577da2c3c220c4c1e1f6198e5b3b440813fb66eca3ed74 languageName: node linkType: hard -"@tanstack/query-core@npm:4.29.25": - version: 4.29.25 - resolution: "@tanstack/query-core@npm:4.29.25" - checksum: 5287e278cf0ef781c5bd238842243adc4430a43ffc9bee1963131726c5bdf6a67d38e122f45c3375232d05c5a335b1309026ce4252647bb3e4710b70bcebdbf5 +"@tanstack/query-core@npm:4.32.0": + version: 4.32.0 + resolution: "@tanstack/query-core@npm:4.32.0" + checksum: 7b3d965d634ff7e754d5f36fe2e4bd9a34548c1d7b13161a2f1a84c8eaafa835f8193e9494952ae20d330f3b56680a5ef516228b2d85b5bc5a34a49947223e58 languageName: node linkType: hard -"@tanstack/query-persist-client-core@npm:4.29.25": - version: 4.29.25 - resolution: "@tanstack/query-persist-client-core@npm:4.29.25" +"@tanstack/query-persist-client-core@npm:4.32.0": + version: 4.32.0 + resolution: "@tanstack/query-persist-client-core@npm:4.32.0" dependencies: - "@tanstack/query-core": 4.29.25 - checksum: 688c4eea630585fb885f048f25d8124e334a2197efea657a9f257d88e0cb7d3cdbaf6e88f9dae0aeb1a0f9b0413d2ce736f37c73f48f50064b7460b803da4bbc + "@tanstack/query-core": 4.32.0 + checksum: 1a29dc33318c7c6b350f6c51220e8ce02174a30f0f4c9e419cdfe5c802432239aa4a5ccff17b11905c0928d126d0d5aa39eeb1b62e558e5ad4bff9ef9137099e languageName: node linkType: hard "@tanstack/query-sync-storage-persister@npm:^4.27.1": - version: 4.29.25 - resolution: "@tanstack/query-sync-storage-persister@npm:4.29.25" + version: 4.32.0 + resolution: "@tanstack/query-sync-storage-persister@npm:4.32.0" dependencies: - "@tanstack/query-persist-client-core": 4.29.25 - checksum: d453311555e32380fc8984c3e85364174d8c361032db930bb05e4825b811b9c3f71bbbfe72a512ff453ca3a73c6e8a34ca1eb5b77ebd6e8b84f252f9d3cc45a1 + "@tanstack/query-persist-client-core": 4.32.0 + checksum: 752f884c9525d58cb531fcfdf37938a4eb8aea8f613e5f45f572cf4c4f3c2f25fc9b2b4f3dedffa1cad704e32d570f0617c4d67e9c6fa750281f6c3caeb72430 languageName: node linkType: hard "@tanstack/react-query-devtools@npm:^4.24.4": - version: 4.29.25 - resolution: "@tanstack/react-query-devtools@npm:4.29.25" + version: 4.32.0 + resolution: "@tanstack/react-query-devtools@npm:4.32.0" dependencies: "@tanstack/match-sorter-utils": ^8.7.0 superjson: ^1.10.0 use-sync-external-store: ^1.2.0 peerDependencies: - "@tanstack/react-query": 4.29.25 + "@tanstack/react-query": 4.32.0 react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: 2861f78de92a5c7595221383d9e60a70eafd0b1ed08313b624697fb221a1e6694200c14f4f5c3c2ef9ee0b1a525f7b7f04d3e0587b32b7a73cfb2d466d3e98af + checksum: 5d8d55043e8dddf8cb3361ec4f66ec07da61343b0f522c20991ce6718f45206659be6d4ab90792aec4ea6862228fe0ada792313c5b238803aa78dbbefe93b194 languageName: node linkType: hard "@tanstack/react-query-persist-client@npm:^4.28.0": - version: 4.29.25 - resolution: "@tanstack/react-query-persist-client@npm:4.29.25" + version: 4.32.0 + resolution: "@tanstack/react-query-persist-client@npm:4.32.0" dependencies: - "@tanstack/query-persist-client-core": 4.29.25 + "@tanstack/query-persist-client-core": 4.32.0 peerDependencies: - "@tanstack/react-query": 4.29.25 - checksum: 8f19e38fd09f87646a9f25ba20c7513cbbf5ed8efc6a9fe75c696df7d159c80a807f24fda47082c4a881715f300f85ab94b6f147709e0fa9dbd04696270913fd + "@tanstack/react-query": 4.32.0 + checksum: 7d1bce9cc92a5fac35a403b886a542fee14a76c3a331acff4c7cbff870b249c8cadc964958ce1cf666cd1802b0fcaa0565792f6a5d34a70332526e793e204276 languageName: node linkType: hard "@tanstack/react-query@npm:^4.2.1": - version: 4.29.25 - resolution: "@tanstack/react-query@npm:4.29.25" + version: 4.32.0 + resolution: "@tanstack/react-query@npm:4.32.0" dependencies: - "@tanstack/query-core": 4.29.25 + "@tanstack/query-core": 4.32.0 use-sync-external-store: ^1.2.0 peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -1903,23 +1906,23 @@ __metadata: optional: true react-native: optional: true - checksum: e0ae4cbe1d8f691d488bc1c68df3df12d1ed093796eb879842ef8d4d388d07cebbdd7ab6174481d624dc133326895a53a5442864bb27ffcc0db5f4f40d7f827f + checksum: fe9c9573a6132961d0437ca3b8d9e379553e0ddd1e884d75dd34c98d5e64e4305e45119d95aec4a7d56aae7bf09cb2792fd9e75da731f5c14db5e43b36732d48 languageName: node linkType: hard "@testing-library/dom@npm:^9.0.0": - version: 9.3.0 - resolution: "@testing-library/dom@npm:9.3.0" + version: 9.3.1 + resolution: "@testing-library/dom@npm:9.3.1" dependencies: "@babel/code-frame": ^7.10.4 "@babel/runtime": ^7.12.5 "@types/aria-query": ^5.0.1 - aria-query: ^5.0.0 + aria-query: 5.1.3 chalk: ^4.1.0 dom-accessibility-api: ^0.5.9 lz-string: ^1.5.0 pretty-format: ^27.0.2 - checksum: 790f4da6a8cbe7da8b7769e81e68caea1aed5b5f1973b808895692a945fb854fe8acdc66ffc34b6a57ec49bad9d76ccdd69b632ea8a82ad61d1e97d76cfdf9ec + checksum: 8ee3136451644e39990edea93709c38cf1e8ce5306f3c66273ca00935963faa51ca74e8d92b02eb442ccb842cfa28ca62833e393e075eb269cf9bef6f5600663 languageName: node linkType: hard @@ -1982,48 +1985,48 @@ __metadata: linkType: hard "@trpc/client@npm:^10.29.1": - version: 10.34.0 - resolution: "@trpc/client@npm:10.34.0" + version: 10.36.0 + resolution: "@trpc/client@npm:10.36.0" peerDependencies: - "@trpc/server": 10.34.0 - checksum: 7589d575d92865091da72bf0eb7efbfc474422aecc2d4ac146be334bbe83a2a67eb185654dccb91b90e23207bfada2b9f4e4ebcc25a24c2a7e033c177f13e10c + "@trpc/server": 10.36.0 + checksum: 161271d9d5ede59a29daa2c974452945fb1b8bc0613a7d26d2cf6e7686faef4a421eefcdae75d28b8b68f6584b4e7cb0583de80b3635419ae74ea17c04bb6b90 languageName: node linkType: hard "@trpc/next@npm:^10.29.1": - version: 10.34.0 - resolution: "@trpc/next@npm:10.34.0" + version: 10.36.0 + resolution: "@trpc/next@npm:10.36.0" dependencies: react-ssr-prepass: ^1.5.0 peerDependencies: "@tanstack/react-query": ^4.18.0 - "@trpc/client": 10.34.0 - "@trpc/react-query": 10.34.0 - "@trpc/server": 10.34.0 + "@trpc/client": 10.36.0 + "@trpc/react-query": 10.36.0 + "@trpc/server": 10.36.0 next: "*" react: ">=16.8.0" react-dom: ">=16.8.0" - checksum: aa0970f02c746b8060ccea72f685186e6c8209f5af32b35607899e8da3d1ba20832cb1506f093f70dbdbc17993db8522646a45a1f453d12f5913cf69912d7643 + checksum: e62feec77ac86e3c403f6a67c8183ea0c56deb7ba46a09a0eb03b36a04f64eadf5aa3f93047047b1d3fb158b2b2439f1e060c34db928592708c1d54f6edaeb30 languageName: node linkType: hard "@trpc/react-query@npm:^10.29.1": - version: 10.34.0 - resolution: "@trpc/react-query@npm:10.34.0" + version: 10.36.0 + resolution: "@trpc/react-query@npm:10.36.0" peerDependencies: "@tanstack/react-query": ^4.18.0 - "@trpc/client": 10.34.0 - "@trpc/server": 10.34.0 + "@trpc/client": 10.36.0 + "@trpc/server": 10.36.0 react: ">=16.8.0" react-dom: ">=16.8.0" - checksum: 5489f4041da444795d59b8b766ba0924569317f62ab5a85411ff4bfde2b37e2d30e590fb4041dc7f799f519d6e28f5e120b16fd9066ffcb1f477d17abbe216ec + checksum: 1756488d02ba409bd441f8be27188186a3d5690432f943eb8e4b57c3db3f362e92a6d61be03a32073fd0b9ede0e0656b65aed082949c675c56a81b6ec88f83da languageName: node linkType: hard "@trpc/server@npm:^10.29.1": - version: 10.34.0 - resolution: "@trpc/server@npm:10.34.0" - checksum: 6d319c98f8a152539cd46769884ddea01bf00daaf1780067b1a1e34b45a107ab702b72e0086fa8924be44d9211b51c8f4e53ab6e85583fa53fa5893df5847c62 + version: 10.36.0 + resolution: "@trpc/server@npm:10.36.0" + checksum: 0ced75a537986a35d8a69e5a3c7d0c8198a6042db210200536ddc1765f61302169861c39d4364507ef7845b62c88a2266cb93998b92aaeb8cead292af520546a languageName: node linkType: hard @@ -2312,16 +2315,16 @@ __metadata: linkType: hard "@types/jest@npm:*": - version: 29.5.2 - resolution: "@types/jest@npm:29.5.2" + version: 29.5.3 + resolution: "@types/jest@npm:29.5.3" dependencies: expect: ^29.0.0 pretty-format: ^29.0.0 - checksum: 7d205599ea3cccc262bad5cc173d3242d6bf8138c99458509230e4ecef07a52d6ddcde5a1dbd49ace655c0af51d2dbadef3748697292ea4d86da19d9e03e19c0 + checksum: e36bb92e0b9e5ea7d6f8832baa42f087fc1697f6cd30ec309a07ea4c268e06ec460f1f0cfd2581daf5eff5763475190ec1ad8ac6520c49ccfe4f5c0a48bfa676 languageName: node linkType: hard -"@types/json-schema@npm:^7.0.11, @types/json-schema@npm:^7.0.9": +"@types/json-schema@npm:^7.0.12, @types/json-schema@npm:^7.0.9": version: 7.0.12 resolution: "@types/json-schema@npm:7.0.12" checksum: 00239e97234eeb5ceefb0c1875d98ade6e922bfec39dd365ec6bd360b5c2f825e612ac4f6e5f1d13601b8b30f378f15e6faa805a3a732f4a1bbe61915163d293 @@ -2366,9 +2369,9 @@ __metadata: linkType: hard "@types/node@npm:*": - version: 20.2.5 - resolution: "@types/node@npm:20.2.5" - checksum: 38ce7c7e9d76880dc632f71d71e0d5914fcda9d5e9a7095d6c339abda55ca4affb0f2a882aeb29398f8e09d2c5151f0b6586c81c8ccdfe529c34b1ea3337425e + version: 20.4.5 + resolution: "@types/node@npm:20.4.5" + checksum: 36a0304a8dc346a1b2d2edac4c4633eecf70875793d61a5274d0df052d7a7af7a8e34f29884eac4fbd094c4f0201477dcb39c0ecd3307ca141688806538d1138 languageName: node linkType: hard @@ -2380,16 +2383,16 @@ __metadata: linkType: hard "@types/node@npm:^16.10.2": - version: 16.18.34 - resolution: "@types/node@npm:16.18.34" - checksum: 35c0ffe09687578d002ceb7e706d0ba450546aeb3d2716f28691f2af0063bd274dbde0f741d087ea217f2a8db413eb700d22dfb4f08a67986ff801423bd7be8d + version: 16.18.39 + resolution: "@types/node@npm:16.18.39" + checksum: eac9b202b76013256cb517ca8d3e3f61df206edb1615ca8d8df4c80616e92879fe4d3f8570a11d60f4216a82724a3265d5888b24c6994c80b057a0423c9ff1d2 languageName: node linkType: hard "@types/node@npm:^18.11.18": - version: 18.16.16 - resolution: "@types/node@npm:18.16.16" - checksum: 0efad726dd1e0bef71c392c708fc5d78c5b39c46b0ac5186fee74de4ccb1b2e847b3fa468da67d62812f56569da721b15bf31bdc795e6c69b56c73a45079ed2d + version: 18.17.1 + resolution: "@types/node@npm:18.17.1" + checksum: 56201bda9a2d05d68602df63b4e67b0545ac8c6d0280bd5fb31701350a978a577a027501fbf49db99bf177f2242ebd1244896bfd35e89042d5bd7dfebff28d4e languageName: node linkType: hard @@ -2429,11 +2432,11 @@ __metadata: linkType: hard "@types/react-dom@npm:^18.0.0": - version: 18.2.4 - resolution: "@types/react-dom@npm:18.2.4" + version: 18.2.7 + resolution: "@types/react-dom@npm:18.2.7" dependencies: "@types/react": "*" - checksum: 8301f35cf1cbfec8c723e9477aecf87774e3c168bd457d353b23c45064737213d3e8008b067c6767b7b08e4f2b3823ee239242a6c225fc91e7f8725ef8734124 + checksum: e02ea908289a7ad26053308248d2b87f6aeafd73d0e2de2a3d435947bcea0422599016ffd1c3e38ff36c42f5e1c87c7417f05b0a157e48649e4a02f21727d54f languageName: node linkType: hard @@ -2447,13 +2450,13 @@ __metadata: linkType: hard "@types/react@npm:*, @types/react@npm:^18.2.11": - version: 18.2.15 - resolution: "@types/react@npm:18.2.15" + version: 18.2.17 + resolution: "@types/react@npm:18.2.17" dependencies: "@types/prop-types": "*" "@types/scheduler": "*" csstype: ^3.0.2 - checksum: 36989f638201bfe2f4110b06c119180f6df9c0e13d7060481e82e7a745f81745a01ae543c478a25b61e0767cb52e82da2ad5b0dedacabf99339e523d06176705 + checksum: 150516b31bd98b635e4a56bcf2af007330b35cccb6e35e902f46a47f0e81e30c46cdacc095e91051bdf2f33e4846e7e62fd51b67e064dc7d15c00e15dfa449d5 languageName: node linkType: hard @@ -2473,7 +2476,7 @@ __metadata: languageName: node linkType: hard -"@types/semver@npm:^7.3.12": +"@types/semver@npm:^7.3.12, @types/semver@npm:^7.5.0": version: 7.5.0 resolution: "@types/semver@npm:7.5.0" checksum: 0a64b9b9c7424d9a467658b18dd70d1d781c2d6f033096a6e05762d20ebbad23c1b69b0083b0484722aabf35640b78ccc3de26368bcae1129c87e9df028a22e2 @@ -2502,11 +2505,11 @@ __metadata: linkType: hard "@types/ssh2@npm:*": - version: 1.11.11 - resolution: "@types/ssh2@npm:1.11.11" + version: 1.11.13 + resolution: "@types/ssh2@npm:1.11.13" dependencies: "@types/node": ^18.11.18 - checksum: f020cf301bd63a27eeb228178e3cbacf52da2258cc2238bfdf93f3ff808c1c960be8005eaaaa4347b843b94d461072ce89bb04c04045b4726c862406453e9143 + checksum: 89bfaf9363ca9ca2db8e3ff22e37d2ea21637aec421cac2d54be6b1321fe70250a056646e74e0df0e8c08efa81f7b14a60bb614c24319768655af06165350093 languageName: node linkType: hard @@ -2518,11 +2521,11 @@ __metadata: linkType: hard "@types/testing-library__jest-dom@npm:^5.9.1": - version: 5.14.6 - resolution: "@types/testing-library__jest-dom@npm:5.14.6" + version: 5.14.9 + resolution: "@types/testing-library__jest-dom@npm:5.14.9" dependencies: "@types/jest": "*" - checksum: 92f81cefeacba3b5c06d4b3fbea0341fe2bcaa6e425c026ae262de39f1148c2588cf3003112aa4ac0880c3972ffb77641a863f3be71518d1d8080402c944e326 + checksum: d364494fc2545316292e88861146146af1e3818792ca63b62a63758b2f737669b687f4aaddfcfbcb7d0e1ed7890a9bd05de23ff97f277d5e68de574497a9ee72 languageName: node linkType: hard @@ -2557,21 +2560,20 @@ __metadata: linkType: hard "@typescript-eslint/eslint-plugin@npm:^6.0.0": - version: 6.0.0 - resolution: "@typescript-eslint/eslint-plugin@npm:6.0.0" + version: 6.2.0 + resolution: "@typescript-eslint/eslint-plugin@npm:6.2.0" dependencies: - "@eslint-community/regexpp": ^4.5.0 - "@typescript-eslint/scope-manager": 6.0.0 - "@typescript-eslint/type-utils": 6.0.0 - "@typescript-eslint/utils": 6.0.0 - "@typescript-eslint/visitor-keys": 6.0.0 + "@eslint-community/regexpp": ^4.5.1 + "@typescript-eslint/scope-manager": 6.2.0 + "@typescript-eslint/type-utils": 6.2.0 + "@typescript-eslint/utils": 6.2.0 + "@typescript-eslint/visitor-keys": 6.2.0 debug: ^4.3.4 - grapheme-splitter: ^1.0.4 graphemer: ^1.4.0 ignore: ^5.2.4 natural-compare: ^1.4.0 natural-compare-lite: ^1.4.0 - semver: ^7.5.0 + semver: ^7.5.4 ts-api-utils: ^1.0.1 peerDependencies: "@typescript-eslint/parser": ^6.0.0 || ^6.0.0-alpha @@ -2579,7 +2581,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 863f30b8ceb24d104fc8a41774e4f597a35525533aa99721198293b51628a2d986dcc6413893f27eb9db5a49c2fd2cc91d3aece8ed23d590f3eb4e9939c3d6ad + checksum: 1ef46b1c2e3e2013f66b4982dcfb9e198a3824cc1503b843e553201a108a3cb6e4adfb2c486158c89d993e5e4b9d99aeb2af28297e43da98c4750dae8f5131b5 languageName: node linkType: hard @@ -2601,30 +2603,20 @@ __metadata: linkType: hard "@typescript-eslint/parser@npm:^6.0.0": - version: 6.0.0 - resolution: "@typescript-eslint/parser@npm:6.0.0" + version: 6.2.0 + resolution: "@typescript-eslint/parser@npm:6.2.0" dependencies: - "@typescript-eslint/scope-manager": 6.0.0 - "@typescript-eslint/types": 6.0.0 - "@typescript-eslint/typescript-estree": 6.0.0 - "@typescript-eslint/visitor-keys": 6.0.0 + "@typescript-eslint/scope-manager": 6.2.0 + "@typescript-eslint/types": 6.2.0 + "@typescript-eslint/typescript-estree": 6.2.0 + "@typescript-eslint/visitor-keys": 6.2.0 debug: ^4.3.4 peerDependencies: eslint: ^7.0.0 || ^8.0.0 peerDependenciesMeta: typescript: optional: true - checksum: a22f0c8f67eb244134f9d79d78faf1b6e2c0965495d78eef94a5680868f3d0fd9446a3ce5dc1e36dde02587da5d962944f3d83679c712d0b819ac99cdb9f7143 - languageName: node - linkType: hard - -"@typescript-eslint/scope-manager@npm:5.59.9": - version: 5.59.9 - resolution: "@typescript-eslint/scope-manager@npm:5.59.9" - dependencies: - "@typescript-eslint/types": 5.59.9 - "@typescript-eslint/visitor-keys": 5.59.9 - checksum: 362c22662d844440a7e14223d8cc0722f77ff21ad8f78deb0ee3b3f21de01b8846bf25fbbf527544677e83d8ff48008b3f7d40b39ddec55994ea4a1863e9ec0a + checksum: ba79674f2d4599a24c7afa8f18ec28243b80df39f82a4a6b7a4ce7c584ec37d4ade40a3aa058d597a5cbf71647a40d0995866748d14cf4b52d8ad4420d10f669 languageName: node linkType: hard @@ -2638,22 +2630,22 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:6.0.0": - version: 6.0.0 - resolution: "@typescript-eslint/scope-manager@npm:6.0.0" +"@typescript-eslint/scope-manager@npm:6.2.0": + version: 6.2.0 + resolution: "@typescript-eslint/scope-manager@npm:6.2.0" dependencies: - "@typescript-eslint/types": 6.0.0 - "@typescript-eslint/visitor-keys": 6.0.0 - checksum: 450015be6454f953d0ea0da020ab47597e96a7a15c1002eed16c57430783bd7b045513d57a126606fb35e8971f1ce65fbefd845e3b5496bf75284cbe1681d0b9 + "@typescript-eslint/types": 6.2.0 + "@typescript-eslint/visitor-keys": 6.2.0 + checksum: 75a650a3ede78bf841a3bf3f4880b94a06aa4c420f399a6fb9faee19a2e5998f7e330a13f78e07c4958413345bab58b0593f09fa163a77e8f6353012e795660c languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:6.0.0": - version: 6.0.0 - resolution: "@typescript-eslint/type-utils@npm:6.0.0" +"@typescript-eslint/type-utils@npm:6.2.0": + version: 6.2.0 + resolution: "@typescript-eslint/type-utils@npm:6.2.0" dependencies: - "@typescript-eslint/typescript-estree": 6.0.0 - "@typescript-eslint/utils": 6.0.0 + "@typescript-eslint/typescript-estree": 6.2.0 + "@typescript-eslint/utils": 6.2.0 debug: ^4.3.4 ts-api-utils: ^1.0.1 peerDependencies: @@ -2661,14 +2653,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 53f46237891cfa738f6a4bc766a4dbb8d745b1cb9cbe2d2b40f2a4abcf0327d4aa92d9ce5361e87cd26d82e0159f358e28b0c67759eb053c4fd752654dc9dcb1 - languageName: node - linkType: hard - -"@typescript-eslint/types@npm:5.59.9": - version: 5.59.9 - resolution: "@typescript-eslint/types@npm:5.59.9" - checksum: 283f8fee1ee590eeccc2e0fcd3526c856c4b1e2841af2cdcd09eeac842a42cfb32f6bc8b40385380f3dbc3ee29da30f1819115eedf9e16f69ff5a160aeddd8fa + checksum: 9adb542fb3c49bf5c1fecca98549bee3fcfd28a0ceee5227817a1ceb0841b912e322f58ba1b3ca98a47fc998cbec0a3d69cacb9cf9ac4be1d133b11bb9d53aae languageName: node linkType: hard @@ -2679,28 +2664,10 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/types@npm:6.0.0": - version: 6.0.0 - resolution: "@typescript-eslint/types@npm:6.0.0" - checksum: a2e232b66b0b057152f4a94d7e0be75f32e389c9c1ec9ed9901ed5aab6e5df08c07bde9865710e315d835e4400ec2232f9c3c525b6edf8a85675ebfbfb69d3a5 - languageName: node - linkType: hard - -"@typescript-eslint/typescript-estree@npm:5.59.9": - version: 5.59.9 - resolution: "@typescript-eslint/typescript-estree@npm:5.59.9" - dependencies: - "@typescript-eslint/types": 5.59.9 - "@typescript-eslint/visitor-keys": 5.59.9 - debug: ^4.3.4 - globby: ^11.1.0 - is-glob: ^4.0.3 - semver: ^7.3.7 - tsutils: ^3.21.0 - peerDependenciesMeta: - typescript: - optional: true - checksum: c0c9b81f20a2a4337f07bc3ccdc9c1dabd765f59096255ed9a149e91e5c9517b25c2b6655f8f073807cfc13500c7451fbd9bb62e5e572c07cc07945ab042db89 +"@typescript-eslint/types@npm:6.2.0": + version: 6.2.0 + resolution: "@typescript-eslint/types@npm:6.2.0" + checksum: 81878866cf7f49dbc335cce05adfbd994f348e2ebe9538fd6e934fa82e44186c16b2112b8d5f9f4c528ea127be157185be5e35e4913db4880d20ac495785baaf languageName: node linkType: hard @@ -2722,67 +2689,56 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:6.0.0": - version: 6.0.0 - resolution: "@typescript-eslint/typescript-estree@npm:6.0.0" +"@typescript-eslint/typescript-estree@npm:6.2.0": + version: 6.2.0 + resolution: "@typescript-eslint/typescript-estree@npm:6.2.0" dependencies: - "@typescript-eslint/types": 6.0.0 - "@typescript-eslint/visitor-keys": 6.0.0 + "@typescript-eslint/types": 6.2.0 + "@typescript-eslint/visitor-keys": 6.2.0 debug: ^4.3.4 globby: ^11.1.0 is-glob: ^4.0.3 - semver: ^7.5.0 + semver: ^7.5.4 ts-api-utils: ^1.0.1 peerDependenciesMeta: typescript: optional: true - checksum: 6214ff9cc3c4fd7fe03f846e96a498ecf85916083bb60d419bc5a12142cff912670032b1de5ea52ab353ca7eeb4e1cc8fa475a22958b010043c88e274df49859 + checksum: 5bfd5bf09feff6c4807cfa65cf407dd0249f7d487d6820941dd05999ee35cacdabaacadf23c92b90b57920025e93088e93924bc8df41f393ac0366538eb2902f languageName: node linkType: hard -"@typescript-eslint/utils@npm:6.0.0": - version: 6.0.0 - resolution: "@typescript-eslint/utils@npm:6.0.0" +"@typescript-eslint/utils@npm:6.2.0, @typescript-eslint/utils@npm:^6.2.0": + version: 6.2.0 + resolution: "@typescript-eslint/utils@npm:6.2.0" dependencies: - "@eslint-community/eslint-utils": ^4.3.0 - "@types/json-schema": ^7.0.11 - "@types/semver": ^7.3.12 - "@typescript-eslint/scope-manager": 6.0.0 - "@typescript-eslint/types": 6.0.0 - "@typescript-eslint/typescript-estree": 6.0.0 - eslint-scope: ^5.1.1 - semver: ^7.5.0 + "@eslint-community/eslint-utils": ^4.4.0 + "@types/json-schema": ^7.0.12 + "@types/semver": ^7.5.0 + "@typescript-eslint/scope-manager": 6.2.0 + "@typescript-eslint/types": 6.2.0 + "@typescript-eslint/typescript-estree": 6.2.0 + semver: ^7.5.4 peerDependencies: eslint: ^7.0.0 || ^8.0.0 - checksum: 94b9b616282f6fa1ae50ba371a482a3c8c50268ef8039b4e86d29c445e95025c819358a5cc9955c4668482d97ef026e7a49e7f4b3a4685347136ef5bbd297e4d + checksum: 54f062412a8ce23554ca4063d275327981640426b1ecd1073d30dd8b9464ff7af68b8f9f6272033bad9307815d56f2f922faa8a995421efdccd6165dd62557e1 languageName: node linkType: hard -"@typescript-eslint/utils@npm:^5.58.0, @typescript-eslint/utils@npm:^5.59.9": - version: 5.59.9 - resolution: "@typescript-eslint/utils@npm:5.59.9" +"@typescript-eslint/utils@npm:^5.58.0": + version: 5.62.0 + resolution: "@typescript-eslint/utils@npm:5.62.0" dependencies: "@eslint-community/eslint-utils": ^4.2.0 "@types/json-schema": ^7.0.9 "@types/semver": ^7.3.12 - "@typescript-eslint/scope-manager": 5.59.9 - "@typescript-eslint/types": 5.59.9 - "@typescript-eslint/typescript-estree": 5.59.9 + "@typescript-eslint/scope-manager": 5.62.0 + "@typescript-eslint/types": 5.62.0 + "@typescript-eslint/typescript-estree": 5.62.0 eslint-scope: ^5.1.1 semver: ^7.3.7 peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - checksum: 22ec5962886de7dcf65f99c37aad9fb189a3bef6b2b07c81887fb82a0e8bf137246da58e64fb02141352285708440be13acd7f6db1ca19e96f86724813ac4646 - languageName: node - linkType: hard - -"@typescript-eslint/visitor-keys@npm:5.59.9": - version: 5.59.9 - resolution: "@typescript-eslint/visitor-keys@npm:5.59.9" - dependencies: - "@typescript-eslint/types": 5.59.9 - eslint-visitor-keys: ^3.3.0 - checksum: 2909ce761f7fe546592cd3c43e33263d8a5fa619375fd2fdffbc72ffc33e40d6feacafb28c79f36c638fcc2225048e7cc08c61cbac6ca63723dc68610d80e3e6 + checksum: ee9398c8c5db6d1da09463ca7bf36ed134361e20131ea354b2da16a5fdb6df9ba70c62a388d19f6eebb421af1786dbbd79ba95ddd6ab287324fc171c3e28d931 languageName: node linkType: hard @@ -2796,13 +2752,13 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:6.0.0": - version: 6.0.0 - resolution: "@typescript-eslint/visitor-keys@npm:6.0.0" +"@typescript-eslint/visitor-keys@npm:6.2.0": + version: 6.2.0 + resolution: "@typescript-eslint/visitor-keys@npm:6.2.0" dependencies: - "@typescript-eslint/types": 6.0.0 + "@typescript-eslint/types": 6.2.0 eslint-visitor-keys: ^3.4.1 - checksum: b0d9848a4490174db1d25b5f336548bb11dde4e0ce664c3dc341bed89fb3a3ada091aeb7f5d2d371433815332d93339c6cb77f7a24469c329c3d055b15237bfa + checksum: b400c657c7e5c65b289304f6f5cee6536f23b3441306f82aff2d2e047e13770330715d4f7b29e734b0b2dab6030e41028894d5cd441696115bfea43ad18b2c54 languageName: node linkType: hard @@ -2957,9 +2913,9 @@ __metadata: linkType: hard "@xmldom/xmldom@npm:^0.8.3": - version: 0.8.8 - resolution: "@xmldom/xmldom@npm:0.8.8" - checksum: 5f5fc0482fcc599f62e3009516932a265e00f1bb2093fe2c76f3f8d9bfebdd13246f48d4132c9b301c7a573f0fa8712e56aa747dce75b179c2b73f1dde7b5f42 + version: 0.8.10 + resolution: "@xmldom/xmldom@npm:0.8.10" + checksum: 4c136aec31fb3b49aaa53b6fcbfe524d02a1dc0d8e17ee35bd3bf35e9ce1344560481cd1efd086ad1a4821541482528672306d5e37cdbd187f33d7fadd3e2cf0 languageName: node linkType: hard @@ -2996,21 +2952,12 @@ __metadata: languageName: node linkType: hard -"acorn@npm:^8.0.4, acorn@npm:^8.4.1, acorn@npm:^8.8.2": - version: 8.8.2 - resolution: "acorn@npm:8.8.2" +"acorn@npm:^8.0.4, acorn@npm:^8.4.1, acorn@npm:^8.8.2, acorn@npm:^8.9.0": + version: 8.10.0 + resolution: "acorn@npm:8.10.0" bin: acorn: bin/acorn - checksum: f790b99a1bf63ef160c967e23c46feea7787e531292bb827126334612c234ed489a0dc2c7ba33156416f0ffa8d25bf2b0fdb7f35c2ba60eb3e960572bece4001 - languageName: node - linkType: hard - -"acorn@npm:^8.9.0": - version: 8.9.0 - resolution: "acorn@npm:8.9.0" - bin: - acorn: bin/acorn - checksum: 25dfb94952386ecfb847e61934de04a4e7c2dc21c2e700fc4e2ef27ce78cb717700c4c4f279cd630bb4774948633c3859fc16063ec8573bda4568e0a312e6744 + checksum: 538ba38af0cc9e5ef983aee196c4b8b4d87c0c94532334fa7e065b2c8a1f85863467bb774231aae91613fcda5e68740c15d97b1967ae3394d20faddddd8af61d languageName: node linkType: hard @@ -3056,7 +3003,7 @@ __metadata: languageName: node linkType: hard -"ajv@npm:^6.10.0, ajv@npm:^6.12.4": +"ajv@npm:^6.12.4": version: 6.12.6 resolution: "ajv@npm:6.12.6" dependencies: @@ -3075,6 +3022,13 @@ __metadata: languageName: node linkType: hard +"ansi-regex@npm:^6.0.1": + version: 6.0.1 + resolution: "ansi-regex@npm:6.0.1" + checksum: 1ff8b7667cded1de4fa2c9ae283e979fc87036864317da86a2e546725f96406746411d0d85e87a2d12fa5abd715d90006de7fa4fa0477c92321ad3b4c7d4e169 + languageName: node + linkType: hard + "ansi-styles@npm:^3.2.1": version: 3.2.1 resolution: "ansi-styles@npm:3.2.1" @@ -3100,6 +3054,13 @@ __metadata: languageName: node linkType: hard +"ansi-styles@npm:^6.1.0": + version: 6.2.1 + resolution: "ansi-styles@npm:6.2.1" + checksum: ef940f2f0ced1a6347398da88a91da7930c33ecac3c77b72c5905f8b8fe402c52e6fde304ff5347f616e27a742da3f1dc76de98f6866c69251ad0b07a66776d9 + languageName: node + linkType: hard + "anymatch@npm:~3.1.2": version: 3.1.3 resolution: "anymatch@npm:3.1.3" @@ -3160,7 +3121,7 @@ __metadata: languageName: node linkType: hard -"aria-query@npm:^5.0.0, aria-query@npm:^5.1.3": +"aria-query@npm:5.1.3": version: 5.1.3 resolution: "aria-query@npm:5.1.3" dependencies: @@ -3169,6 +3130,15 @@ __metadata: languageName: node linkType: hard +"aria-query@npm:^5.0.0, aria-query@npm:^5.1.3": + version: 5.3.0 + resolution: "aria-query@npm:5.3.0" + dependencies: + dequal: ^2.0.3 + checksum: 305bd73c76756117b59aba121d08f413c7ff5e80fa1b98e217a3443fcddb9a232ee790e24e432b59ae7625aebcf4c47cb01c2cac872994f0b426f5bdfcd96ba9 + languageName: node + linkType: hard + "array-buffer-byte-length@npm:^1.0.0": version: 1.0.0 resolution: "array-buffer-byte-length@npm:1.0.0" @@ -3179,7 +3149,7 @@ __metadata: languageName: node linkType: hard -"array-includes@npm:^3.1.5, array-includes@npm:^3.1.6": +"array-includes@npm:^3.1.6": version: 3.1.6 resolution: "array-includes@npm:3.1.6" dependencies: @@ -3199,6 +3169,19 @@ __metadata: languageName: node linkType: hard +"array.prototype.findlastindex@npm:^1.2.2": + version: 1.2.2 + resolution: "array.prototype.findlastindex@npm:1.2.2" + dependencies: + call-bind: ^1.0.2 + define-properties: ^1.1.4 + es-abstract: ^1.20.4 + es-shim-unscopables: ^1.0.0 + get-intrinsic: ^1.1.3 + checksum: 8a166359f69a2a751c843f26b9c8cd03d0dc396a92cdcb85f4126b5f1cecdae5b2c0c616a71ea8aff026bde68165b44950b3664404bb73db0673e288495ba264 + languageName: node + linkType: hard + "array.prototype.flat@npm:^1.3.1": version: 1.3.1 resolution: "array.prototype.flat@npm:1.3.1" @@ -3236,6 +3219,20 @@ __metadata: languageName: node linkType: hard +"arraybuffer.prototype.slice@npm:^1.0.1": + version: 1.0.1 + resolution: "arraybuffer.prototype.slice@npm:1.0.1" + dependencies: + array-buffer-byte-length: ^1.0.0 + call-bind: ^1.0.2 + define-properties: ^1.2.0 + get-intrinsic: ^1.2.1 + is-array-buffer: ^3.0.2 + is-shared-array-buffer: ^1.0.2 + checksum: e3e9b2a3e988ebfeddce4c7e8f69df730c9e48cb04b0d40ff0874ce3d86b3d1339dd520ffde5e39c02610bc172ecfbd4bc93324b1cabd9554c44a56b131ce0ce + languageName: node + linkType: hard + "asn1@npm:^0.2.6": version: 0.2.6 resolution: "asn1@npm:0.2.6" @@ -3299,11 +3296,11 @@ __metadata: linkType: hard "axobject-query@npm:^3.1.1": - version: 3.1.1 - resolution: "axobject-query@npm:3.1.1" + version: 3.2.1 + resolution: "axobject-query@npm:3.2.1" dependencies: - deep-equal: ^2.0.5 - checksum: c12a5da10dc7bab75e1cda9b6a3b5fcf10eba426ddf1a17b71ef65a434ed707ede7d1c4f013ba1609e970bc8c0cddac01365080d376204314e9b294719acd8a5 + dequal: ^2.0.3 + checksum: a94047e702b57c91680e6a952ec4a1aaa2cfd0d80ead76bc8c954202980d8c51968a6ea18b4d8010e8e2cf95676533d8022a8ebba9abc1dfe25686721df26fd2 languageName: node linkType: hard @@ -3413,17 +3410,17 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.21.3": - version: 4.21.7 - resolution: "browserslist@npm:4.21.7" +"browserslist@npm:^4.21.9": + version: 4.21.9 + resolution: "browserslist@npm:4.21.9" dependencies: - caniuse-lite: ^1.0.30001489 - electron-to-chromium: ^1.4.411 + caniuse-lite: ^1.0.30001503 + electron-to-chromium: ^1.4.431 node-releases: ^2.0.12 update-browserslist-db: ^1.0.11 bin: browserslist: cli.js - checksum: 3d0d025e6d381c4db5e71b538258952660ba574c060832095f182a9877ca798836fa550736269e669a2080e486f0cfdf5d3bcf2769b9f7cf123f6c6b8c005f8f + checksum: 80d3820584e211484ad1b1a5cfdeca1dd00442f47be87e117e1dda34b628c87e18b81ae7986fa5977b3e6a03154f6d13cd763baa6b8bf5dd9dd19f4926603698 languageName: node linkType: hard @@ -3498,29 +3495,23 @@ __metadata: languageName: node linkType: hard -"cacache@npm:^16.1.0": - version: 16.1.3 - resolution: "cacache@npm:16.1.3" +"cacache@npm:^17.0.0": + version: 17.1.3 + resolution: "cacache@npm:17.1.3" dependencies: - "@npmcli/fs": ^2.1.0 - "@npmcli/move-file": ^2.0.0 - chownr: ^2.0.0 - fs-minipass: ^2.1.0 - glob: ^8.0.1 - infer-owner: ^1.0.4 + "@npmcli/fs": ^3.1.0 + fs-minipass: ^3.0.0 + glob: ^10.2.2 lru-cache: ^7.7.1 - minipass: ^3.1.6 + minipass: ^5.0.0 minipass-collect: ^1.0.2 minipass-flush: ^1.0.5 minipass-pipeline: ^1.2.4 - mkdirp: ^1.0.4 p-map: ^4.0.0 - promise-inflight: ^1.0.1 - rimraf: ^3.0.2 - ssri: ^9.0.0 + ssri: ^10.0.0 tar: ^6.1.11 - unique-filename: ^2.0.0 - checksum: d91409e6e57d7d9a3a25e5dcc589c84e75b178ae8ea7de05cbf6b783f77a5fae938f6e8fda6f5257ed70000be27a681e1e44829251bfffe4c10216002f8f14e6 + unique-filename: ^3.0.0 + checksum: 385756781e1e21af089160d89d7462b7ed9883c978e848c7075b90b73cb823680e66092d61513050164588387d2ca87dd6d910e28d64bc13a9ac82cd8580c796 languageName: node linkType: hard @@ -3539,17 +3530,17 @@ __metadata: linkType: hard "cacheable-request@npm:^10.2.8": - version: 10.2.10 - resolution: "cacheable-request@npm:10.2.10" + version: 10.2.13 + resolution: "cacheable-request@npm:10.2.13" dependencies: "@types/http-cache-semantics": ^4.0.1 get-stream: ^6.0.1 http-cache-semantics: ^4.1.1 - keyv: ^4.5.2 + keyv: ^4.5.3 mimic-response: ^4.0.0 normalize-url: ^8.0.0 responselike: ^3.0.0 - checksum: 6f56cf6dc88c000936c89e386fdfd65c9a7833f6a4f73314f546287352efca50ef8c7ccc80c64d5c51fe104f5a60356366e190846f56abf3f2e90c1bacec7eee + checksum: 1a2e9a20558ff2e23156bf945110f16d08037830a57c7b97ba9a145f6526fff1e1da21b1a1f9f4ee5fda77a482374e1a537b60dc23dab5df506f5a1cea5be9ab languageName: node linkType: hard @@ -3585,10 +3576,10 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.30001406, caniuse-lite@npm:^1.0.30001489": - version: 1.0.30001497 - resolution: "caniuse-lite@npm:1.0.30001497" - checksum: 6721120f9a588c442a81cf32f911b4e97a88cb129c27bd2cb0fce6447ad058baa12affa1ee09c517f9e088c7ce74964154d032b6631f66d75dd37c6bc59a67f6 +"caniuse-lite@npm:^1.0.30001406, caniuse-lite@npm:^1.0.30001503": + version: 1.0.30001517 + resolution: "caniuse-lite@npm:1.0.30001517" + checksum: e4e87436ae1c4408cf4438aac22902b31eb03f3f5bad7f33bc518d12ffb35f3fd9395ccf7efc608ee046f90ce324ec6f7f26f8a8172b8c43c26a06ecee612a29 languageName: node linkType: hard @@ -3879,9 +3870,9 @@ __metadata: linkType: hard "core-js@npm:^3": - version: 3.30.2 - resolution: "core-js@npm:3.30.2" - checksum: 73d47e2b9d9f502800973982d08e995bbf04832e20b04e04be31dd7607247158271315e9328788a2408190e291c7ffbefad141167b1e57dea9f983e1e723541e + version: 3.32.0 + resolution: "core-js@npm:3.32.0" + checksum: 52921395028550e4c9d21d47b9836439bb5b6b9eefc34d45a3948a68d81fdd093acc0fadf69f9cf632b82f01f95f22f484408a93dd9e940b19119ac204cd2925 languageName: node linkType: hard @@ -3905,7 +3896,7 @@ __metadata: languageName: node linkType: hard -"cpu-features@npm:~0.0.7": +"cpu-features@npm:~0.0.8": version: 0.0.8 resolution: "cpu-features@npm:0.0.8" dependencies: @@ -3924,11 +3915,11 @@ __metadata: linkType: hard "cross-fetch@npm:^3.0.6": - version: 3.1.6 - resolution: "cross-fetch@npm:3.1.6" + version: 3.1.8 + resolution: "cross-fetch@npm:3.1.8" dependencies: - node-fetch: ^2.6.11 - checksum: 704b3519ab7de488328cc49a52cf1aa14132ec748382be5b9557b22398c33ffa7f8c2530e8a97ed8cb55da52b0a9740a9791d361271c4591910501682d981d9c + node-fetch: ^2.6.12 + checksum: 78f993fa099eaaa041122ab037fe9503ecbbcb9daef234d1d2e0b9230a983f64d645d088c464e21a247b825a08dc444a6e7064adfa93536d3a9454b4745b3632 languageName: node linkType: hard @@ -4151,13 +4142,13 @@ __metadata: linkType: hard "deep-equal@npm:^2.0.5": - version: 2.2.1 - resolution: "deep-equal@npm:2.2.1" + version: 2.2.2 + resolution: "deep-equal@npm:2.2.2" dependencies: array-buffer-byte-length: ^1.0.0 call-bind: ^1.0.2 es-get-iterator: ^1.1.3 - get-intrinsic: ^1.2.0 + get-intrinsic: ^1.2.1 is-arguments: ^1.1.1 is-array-buffer: ^3.0.2 is-date-object: ^1.0.5 @@ -4172,7 +4163,7 @@ __metadata: which-boxed-primitive: ^1.0.2 which-collection: ^1.0.1 which-typed-array: ^1.1.9 - checksum: 561f0e001a07b2f1b80ff914d0b3d76964bbfc102f34c2128bc8039c0050e63b1a504a8911910e011d8cd1cd4b600a9686c049e327f4ef94420008efc42d25f4 + checksum: eb61c35157b6ecb96a5359b507b083fbff8ddb4c86a78a781ee38485f77a667465e45d63ee2ebd8a00e86d94c80e499906900cd82c2debb400237e1662cd5397 languageName: node linkType: hard @@ -4264,6 +4255,13 @@ __metadata: languageName: node linkType: hard +"dequal@npm:^2.0.3": + version: 2.0.3 + resolution: "dequal@npm:2.0.3" + checksum: 8679b850e1a3d0ebbc46ee780d5df7b478c23f335887464023a631d1b9af051ad4a6595a44220f9ff8ff95a8ddccf019b5ad778a976fd7bbf77383d36f412f90 + languageName: node + linkType: hard + "detect-libc@npm:^2.0.0": version: 2.0.2 resolution: "detect-libc@npm:2.0.2" @@ -4420,10 +4418,17 @@ __metadata: languageName: node linkType: hard -"electron-to-chromium@npm:^1.4.411": - version: 1.4.426 - resolution: "electron-to-chromium@npm:1.4.426" - checksum: 9f9d37d7768b6ef1cda5356b6fb308b4a8ce5cd33992a1d7fadb7c0066595dd65ffbf0c17f8b440b16b07d7852d5be4cbbb7d7f1718f6593ef556dbe7b2c2518 +"eastasianwidth@npm:^0.2.0": + version: 0.2.0 + resolution: "eastasianwidth@npm:0.2.0" + checksum: 7d00d7cd8e49b9afa762a813faac332dee781932d6f2c848dc348939c4253f1d4564341b7af1d041853bc3f32c2ef141b58e0a4d9862c17a7f08f68df1e0f1ed + languageName: node + linkType: hard + +"electron-to-chromium@npm:^1.4.431": + version: 1.4.475 + resolution: "electron-to-chromium@npm:1.4.475" + checksum: 3e7a538c486b351c13bcd05295535b2b5b0a2980dcf3504457fe47c6689d74a87fd62eab629227dcc86b66cfd732076667d6392f998b13995a8b54eff8073fb8 languageName: node linkType: hard @@ -4460,12 +4465,12 @@ __metadata: linkType: hard "enhanced-resolve@npm:^5.12.0": - version: 5.14.1 - resolution: "enhanced-resolve@npm:5.14.1" + version: 5.15.0 + resolution: "enhanced-resolve@npm:5.15.0" dependencies: graceful-fs: ^4.2.4 tapable: ^2.2.0 - checksum: ad2a31928b6649eed40d364838449587f731baa63863e83d2629bebaa8be1eabac18b90f89c1784bc805b0818363e99b22547159edd485d7e5ccf18cdc640642 + checksum: fbd8cdc9263be71cc737aa8a7d6c57b43d6aa38f6cc75dde6fcd3598a130cc465f979d2f4d01bb3bf475acb43817749c79f8eef9be048683602ca91ab52e4f11 languageName: node linkType: hard @@ -4513,17 +4518,18 @@ __metadata: languageName: node linkType: hard -"es-abstract@npm:^1.19.0, es-abstract@npm:^1.20.4": - version: 1.21.2 - resolution: "es-abstract@npm:1.21.2" +"es-abstract@npm:^1.19.0, es-abstract@npm:^1.20.4, es-abstract@npm:^1.21.2": + version: 1.22.1 + resolution: "es-abstract@npm:1.22.1" dependencies: array-buffer-byte-length: ^1.0.0 + arraybuffer.prototype.slice: ^1.0.1 available-typed-arrays: ^1.0.5 call-bind: ^1.0.2 es-set-tostringtag: ^2.0.1 es-to-primitive: ^1.2.1 function.prototype.name: ^1.1.5 - get-intrinsic: ^1.2.0 + get-intrinsic: ^1.2.1 get-symbol-description: ^1.0.0 globalthis: ^1.0.3 gopd: ^1.0.1 @@ -4543,15 +4549,19 @@ __metadata: object-inspect: ^1.12.3 object-keys: ^1.1.1 object.assign: ^4.1.4 - regexp.prototype.flags: ^1.4.3 + regexp.prototype.flags: ^1.5.0 + safe-array-concat: ^1.0.0 safe-regex-test: ^1.0.0 string.prototype.trim: ^1.2.7 string.prototype.trimend: ^1.0.6 string.prototype.trimstart: ^1.0.6 + typed-array-buffer: ^1.0.0 + typed-array-byte-length: ^1.0.0 + typed-array-byte-offset: ^1.0.0 typed-array-length: ^1.0.4 unbox-primitive: ^1.0.2 - which-typed-array: ^1.1.9 - checksum: 037f55ee5e1cdf2e5edbab5524095a4f97144d95b94ea29e3611b77d852fd8c8a40e7ae7101fa6a759a9b9b1405f188c3c70928f2d3cd88d543a07fc0d5ad41a + which-typed-array: ^1.1.10 + checksum: 614e2c1c3717cb8d30b6128ef12ea110e06fd7d75ad77091ca1c5dbfb00da130e62e4bbbbbdda190eada098a22b27fe0f99ae5a1171dac2c8663b1e8be8a3a9b languageName: node linkType: hard @@ -4603,32 +4613,32 @@ __metadata: languageName: node linkType: hard -"esbuild@npm:^0.17.5": - version: 0.17.19 - resolution: "esbuild@npm:0.17.19" +"esbuild@npm:^0.18.10": + version: 0.18.17 + resolution: "esbuild@npm:0.18.17" dependencies: - "@esbuild/android-arm": 0.17.19 - "@esbuild/android-arm64": 0.17.19 - "@esbuild/android-x64": 0.17.19 - "@esbuild/darwin-arm64": 0.17.19 - "@esbuild/darwin-x64": 0.17.19 - "@esbuild/freebsd-arm64": 0.17.19 - "@esbuild/freebsd-x64": 0.17.19 - "@esbuild/linux-arm": 0.17.19 - "@esbuild/linux-arm64": 0.17.19 - "@esbuild/linux-ia32": 0.17.19 - "@esbuild/linux-loong64": 0.17.19 - "@esbuild/linux-mips64el": 0.17.19 - "@esbuild/linux-ppc64": 0.17.19 - "@esbuild/linux-riscv64": 0.17.19 - "@esbuild/linux-s390x": 0.17.19 - "@esbuild/linux-x64": 0.17.19 - "@esbuild/netbsd-x64": 0.17.19 - "@esbuild/openbsd-x64": 0.17.19 - "@esbuild/sunos-x64": 0.17.19 - "@esbuild/win32-arm64": 0.17.19 - "@esbuild/win32-ia32": 0.17.19 - "@esbuild/win32-x64": 0.17.19 + "@esbuild/android-arm": 0.18.17 + "@esbuild/android-arm64": 0.18.17 + "@esbuild/android-x64": 0.18.17 + "@esbuild/darwin-arm64": 0.18.17 + "@esbuild/darwin-x64": 0.18.17 + "@esbuild/freebsd-arm64": 0.18.17 + "@esbuild/freebsd-x64": 0.18.17 + "@esbuild/linux-arm": 0.18.17 + "@esbuild/linux-arm64": 0.18.17 + "@esbuild/linux-ia32": 0.18.17 + "@esbuild/linux-loong64": 0.18.17 + "@esbuild/linux-mips64el": 0.18.17 + "@esbuild/linux-ppc64": 0.18.17 + "@esbuild/linux-riscv64": 0.18.17 + "@esbuild/linux-s390x": 0.18.17 + "@esbuild/linux-x64": 0.18.17 + "@esbuild/netbsd-x64": 0.18.17 + "@esbuild/openbsd-x64": 0.18.17 + "@esbuild/sunos-x64": 0.18.17 + "@esbuild/win32-arm64": 0.18.17 + "@esbuild/win32-ia32": 0.18.17 + "@esbuild/win32-x64": 0.18.17 dependenciesMeta: "@esbuild/android-arm": optional: true @@ -4676,7 +4686,7 @@ __metadata: optional: true bin: esbuild: bin/esbuild - checksum: ac11b1a5a6008e4e37ccffbd6c2c054746fc58d0ed4a2f9ee643bd030cfcea9a33a235087bc777def8420f2eaafb3486e76adb7bdb7241a9143b43a69a10afd8 + checksum: c6e1ffa776978a45697763a07ec9b16411db3d3b3997b2c4a0165a211727fce8b63b87165a28d8ef60d3a28b98197bbbc2833e51b89888a4437e0a483dffc8ff languageName: node linkType: hard @@ -4709,10 +4719,10 @@ __metadata: linkType: hard "eslint-config-next@npm:^13.4.5": - version: 13.4.10 - resolution: "eslint-config-next@npm:13.4.10" + version: 13.4.12 + resolution: "eslint-config-next@npm:13.4.12" dependencies: - "@next/eslint-plugin-next": 13.4.10 + "@next/eslint-plugin-next": 13.4.12 "@rushstack/eslint-patch": ^1.1.3 "@typescript-eslint/parser": ^5.42.0 eslint-import-resolver-node: ^0.3.6 @@ -4727,7 +4737,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: d555e077e5045f045e5bdab58429b2245993a0e28d3030209766558a6d32ae165496030cafbab8e6aaf714947d895ee30d1ecdb49263abdb23b6889a333ea714 + checksum: 633eccf920d94a1248c089ec08c52245d863b1b45d68be04deff93c0a0d60373c96610d69d005909b2c9beb81b0af7a12a777d03277cd4db60233e99ac8ef281 languageName: node linkType: hard @@ -4761,7 +4771,7 @@ __metadata: languageName: node linkType: hard -"eslint-module-utils@npm:^2.7.4": +"eslint-module-utils@npm:^2.7.4, eslint-module-utils@npm:^2.8.0": version: 2.8.0 resolution: "eslint-module-utils@npm:2.8.0" dependencies: @@ -4774,27 +4784,30 @@ __metadata: linkType: hard "eslint-plugin-import@npm:^2.26.0": - version: 2.27.5 - resolution: "eslint-plugin-import@npm:2.27.5" + version: 2.28.0 + resolution: "eslint-plugin-import@npm:2.28.0" dependencies: array-includes: ^3.1.6 + array.prototype.findlastindex: ^1.2.2 array.prototype.flat: ^1.3.1 array.prototype.flatmap: ^1.3.1 debug: ^3.2.7 doctrine: ^2.1.0 eslint-import-resolver-node: ^0.3.7 - eslint-module-utils: ^2.7.4 + eslint-module-utils: ^2.8.0 has: ^1.0.3 - is-core-module: ^2.11.0 + is-core-module: ^2.12.1 is-glob: ^4.0.3 minimatch: ^3.1.2 + object.fromentries: ^2.0.6 + object.groupby: ^1.0.0 object.values: ^1.1.6 - resolve: ^1.22.1 - semver: ^6.3.0 - tsconfig-paths: ^3.14.1 + resolve: ^1.22.3 + semver: ^6.3.1 + tsconfig-paths: ^3.14.2 peerDependencies: eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 - checksum: f500571a380167e25d72a4d925ef9a7aae8899eada57653e5f3051ec3d3c16d08271fcefe41a30a9a2f4fefc232f066253673ee4ea77b30dba65ae173dade85d + checksum: f9eba311b93ca1bb89311856b1f7285bd79e0181d7eb70fe115053ff77e2235fea749b30f538b78927dc65769340b5be61f4c9581d1c82bcdcccb2061f440ad1 languageName: node linkType: hard @@ -4852,8 +4865,8 @@ __metadata: linkType: hard "eslint-plugin-react@npm:^7.31.7, eslint-plugin-react@npm:latest": - version: 7.32.2 - resolution: "eslint-plugin-react@npm:7.32.2" + version: 7.33.0 + resolution: "eslint-plugin-react@npm:7.33.0" dependencies: array-includes: ^3.1.6 array.prototype.flatmap: ^1.3.1 @@ -4868,11 +4881,11 @@ __metadata: object.values: ^1.1.6 prop-types: ^15.8.1 resolve: ^2.0.0-next.4 - semver: ^6.3.0 + semver: ^6.3.1 string.prototype.matchall: ^4.0.8 peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 - checksum: 2232b3b8945aa50b7773919c15cd96892acf35d2f82503667a79e2f55def90f728ed4f0e496f0f157acbe1bd4397c5615b676ae7428fe84488a544ca53feb944 + checksum: f3ce2978322efd3c698b802dabfad070109dd1935c4e468545992b82b5fb8257ea3ad56732330bb46643182a09560129a259b436952b3e2aa426947d3abd2b1a languageName: node linkType: hard @@ -4903,13 +4916,17 @@ __metadata: linkType: hard "eslint-plugin-vitest@npm:^0.2.0": - version: 0.2.6 - resolution: "eslint-plugin-vitest@npm:0.2.6" + version: 0.2.8 + resolution: "eslint-plugin-vitest@npm:0.2.8" dependencies: - "@typescript-eslint/utils": ^5.59.9 + "@typescript-eslint/utils": ^6.2.0 peerDependencies: eslint: ">=8.0.0" - checksum: 09bb95262dc39b05546eb704b60440bce03bc68f4d563b3bcb4b25774624b3758db76bfa3702b8b0d45313246b6a6d57729ecc027f64ad840b234b94ca6aa233 + vitest: "*" + peerDependenciesMeta: + vite: + optional: true + checksum: bf09a8da8b564277b7bfcac37d24b3db16b35c61293bccfeb45651a3810c919da74e29e5a264eaf611af35c91b971dbbf28f6fba34166cf6a263d40ad91a48a6 languageName: node linkType: hard @@ -4930,43 +4947,43 @@ __metadata: languageName: node linkType: hard -"eslint-scope@npm:^7.2.0": - version: 7.2.0 - resolution: "eslint-scope@npm:7.2.0" +"eslint-scope@npm:^7.2.2": + version: 7.2.2 + resolution: "eslint-scope@npm:7.2.2" dependencies: esrecurse: ^4.3.0 estraverse: ^5.2.0 - checksum: 64591a2d8b244ade9c690b59ef238a11d5c721a98bcee9e9f445454f442d03d3e04eda88e95a4daec558220a99fa384309d9faae3d459bd40e7a81b4063980ae + checksum: ec97dbf5fb04b94e8f4c5a91a7f0a6dd3c55e46bfc7bbcd0e3138c3a76977570e02ed89a1810c778dcd72072ff0e9621ba1379b4babe53921d71e2e4486fda3e languageName: node linkType: hard -"eslint-visitor-keys@npm:^3.3.0, eslint-visitor-keys@npm:^3.4.1": - version: 3.4.1 - resolution: "eslint-visitor-keys@npm:3.4.1" - checksum: f05121d868202736b97de7d750847a328fcfa8593b031c95ea89425333db59676ac087fa905eba438d0a3c5769632f828187e0c1a0d271832a2153c1d3661c2c +"eslint-visitor-keys@npm:^3.3.0, eslint-visitor-keys@npm:^3.4.1, eslint-visitor-keys@npm:^3.4.2": + version: 3.4.2 + resolution: "eslint-visitor-keys@npm:3.4.2" + checksum: 9e0e7e4aaea705c097ae37c97410e5f167d4d2193be2edcb1f0760762ede3df01545e4820ae314f42dcec687745f2c6dcaf6d83575c4a2a241eb0c8517d724f2 languageName: node linkType: hard "eslint@npm:^8.0.1": - version: 8.45.0 - resolution: "eslint@npm:8.45.0" + version: 8.46.0 + resolution: "eslint@npm:8.46.0" dependencies: "@eslint-community/eslint-utils": ^4.2.0 - "@eslint-community/regexpp": ^4.4.0 - "@eslint/eslintrc": ^2.1.0 - "@eslint/js": 8.44.0 + "@eslint-community/regexpp": ^4.6.1 + "@eslint/eslintrc": ^2.1.1 + "@eslint/js": ^8.46.0 "@humanwhocodes/config-array": ^0.11.10 "@humanwhocodes/module-importer": ^1.0.1 "@nodelib/fs.walk": ^1.2.8 - ajv: ^6.10.0 + ajv: ^6.12.4 chalk: ^4.0.0 cross-spawn: ^7.0.2 debug: ^4.3.2 doctrine: ^3.0.0 escape-string-regexp: ^4.0.0 - eslint-scope: ^7.2.0 - eslint-visitor-keys: ^3.4.1 - espree: ^9.6.0 + eslint-scope: ^7.2.2 + eslint-visitor-keys: ^3.4.2 + espree: ^9.6.1 esquery: ^1.4.2 esutils: ^2.0.2 fast-deep-equal: ^3.1.3 @@ -4990,18 +5007,18 @@ __metadata: text-table: ^0.2.0 bin: eslint: bin/eslint.js - checksum: 3e6dcce5cc43c5e301662db88ee26d1d188b22c177b9f104d7eefd1191236980bd953b3670fe2fac287114b26d7c5420ab48407d7ea1c3a446d6313c000009da + checksum: 7a7d36b1a3bbc12e08fbb5bc36fd482a7a5a1797e62e762499dd45601b9e45aaa53a129f31ce0b4444551a9639b8b681ad535f379893dd1e3ae37b31dccd82aa languageName: node linkType: hard -"espree@npm:^9.6.0": - version: 9.6.0 - resolution: "espree@npm:9.6.0" +"espree@npm:^9.6.0, espree@npm:^9.6.1": + version: 9.6.1 + resolution: "espree@npm:9.6.1" dependencies: acorn: ^8.9.0 acorn-jsx: ^5.3.2 eslint-visitor-keys: ^3.4.1 - checksum: 1287979510efb052a6a97c73067ea5d0a40701b29adde87bbe2d3eb1667e39ca55e8129e20e2517fed3da570150e7ef470585228459a8f3e3755f45007a1c662 + checksum: eb8c149c7a2a77b3f33a5af80c10875c3abd65450f60b8af6db1bfcfa8f101e21c1e56a561c6dc13b848e18148d43469e7cd208506238554fb5395a9ea5a1ab9 languageName: node linkType: hard @@ -5062,8 +5079,8 @@ __metadata: linkType: hard "execa@npm:^7.1.1": - version: 7.1.1 - resolution: "execa@npm:7.1.1" + version: 7.2.0 + resolution: "execa@npm:7.2.0" dependencies: cross-spawn: ^7.0.3 get-stream: ^6.0.1 @@ -5074,20 +5091,28 @@ __metadata: onetime: ^6.0.0 signal-exit: ^3.0.7 strip-final-newline: ^3.0.0 - checksum: 21fa46fc69314ace4068cf820142bdde5b643a5d89831c2c9349479c1555bff137a291b8e749e7efca36535e4e0a8c772c11008ca2e84d2cbd6ca141a3c8f937 + checksum: 14fd17ba0ca8c87b277584d93b1d9fc24f2a65e5152b31d5eb159a3b814854283eaae5f51efa9525e304447e2f757c691877f7adff8fde5746aae67eb1edd1cc languageName: node linkType: hard "expect@npm:^29.0.0": - version: 29.5.0 - resolution: "expect@npm:29.5.0" + version: 29.6.2 + resolution: "expect@npm:29.6.2" dependencies: - "@jest/expect-utils": ^29.5.0 + "@jest/expect-utils": ^29.6.2 + "@types/node": "*" jest-get-type: ^29.4.3 - jest-matcher-utils: ^29.5.0 - jest-message-util: ^29.5.0 - jest-util: ^29.5.0 - checksum: 58f70b38693df6e5c6892db1bcd050f0e518d6f785175dc53917d4fa6a7359a048e5690e19ddcb96b65c4493881dd89a3dabdab1a84dfa55c10cdbdabf37b2d7 + jest-matcher-utils: ^29.6.2 + jest-message-util: ^29.6.2 + jest-util: ^29.6.2 + checksum: 71f7b0c560e58bf6d27e0fded261d4bdb7ef81552a6bb4bd1ee09ce7a1f7dca67fbf83cf9b07a6645a88ef52e65085a0dcbe17f6c063b53ff7c2f0f3ea4ef69e + languageName: node + linkType: hard + +"exponential-backoff@npm:^3.1.1": + version: 3.1.1 + resolution: "exponential-backoff@npm:3.1.1" + checksum: 3d21519a4f8207c99f7457287291316306255a328770d320b401114ec8481986e4e467e854cb9914dd965e0a1ca810a23ccb559c642c88f4c7f55c55778a9b48 languageName: node linkType: hard @@ -5098,29 +5123,16 @@ __metadata: languageName: node linkType: hard -"fast-glob@npm:^3.2.11, fast-glob@npm:^3.2.12, fast-glob@npm:^3.2.9": - version: 3.2.12 - resolution: "fast-glob@npm:3.2.12" +"fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.0": + version: 3.3.1 + resolution: "fast-glob@npm:3.3.1" dependencies: "@nodelib/fs.stat": ^2.0.2 "@nodelib/fs.walk": ^1.2.3 glob-parent: ^5.1.2 merge2: ^1.3.0 micromatch: ^4.0.4 - checksum: 0b1990f6ce831c7e28c4d505edcdaad8e27e88ab9fa65eedadb730438cfc7cde4910d6c975d6b7b8dc8a73da4773702ebcfcd6e3518e73938bb1383badfe01c2 - languageName: node - linkType: hard - -"fast-glob@npm:^3.3.0": - version: 3.3.0 - resolution: "fast-glob@npm:3.3.0" - dependencies: - "@nodelib/fs.stat": ^2.0.2 - "@nodelib/fs.walk": ^1.2.3 - glob-parent: ^5.1.2 - merge2: ^1.3.0 - micromatch: ^4.0.4 - checksum: 20df62be28eb5426fe8e40e0d05601a63b1daceb7c3d87534afcad91bdcf1e4b1743cf2d5247d6e225b120b46df0b9053a032b2691ba34ee121e033acd81f547 + checksum: b6f3add6403e02cf3a798bfbb1183d0f6da2afd368f27456010c0bc1f9640aea308243d4cb2c0ab142f618276e65ecb8be1661d7c62a7b4e5ba774b9ce5432e5 languageName: node linkType: hard @@ -5251,6 +5263,16 @@ __metadata: languageName: node linkType: hard +"foreground-child@npm:^3.1.0": + version: 3.1.1 + resolution: "foreground-child@npm:3.1.1" + dependencies: + cross-spawn: ^7.0.0 + signal-exit: ^4.0.1 + checksum: 139d270bc82dc9e6f8bc045fe2aae4001dc2472157044fdfad376d0a3457f77857fa883c1c8b21b491c6caade9a926a4bed3d3d2e8d3c9202b151a4cbbd0bcd5 + languageName: node + linkType: hard + "form-data-encoder@npm:^2.1.2": version: 2.1.4 resolution: "form-data-encoder@npm:2.1.4" @@ -5280,8 +5302,8 @@ __metadata: linkType: hard "framer-motion@npm:^10.0.0": - version: 10.12.22 - resolution: "framer-motion@npm:10.12.22" + version: 10.15.0 + resolution: "framer-motion@npm:10.15.0" dependencies: "@emotion/is-prop-valid": ^0.8.2 tslib: ^2.4.0 @@ -5296,7 +5318,7 @@ __metadata: optional: true react-dom: optional: true - checksum: ed24d3f92ad3d5578303f25a846921acbd49ccde7de45445fcaaef8e8d77d962cfec8b35426ba9ee26f3fd3af0216bf65d15782c7e0f72babeac59c90a4f571b + checksum: 29f9d4368d7ed8f14e4f4a1979d2071b5745a1575bcf96120f3a4c2ac400a7bec8755139f9439ab19e0fc1225568af1270a88443e75d91bfbe71e21681ce5a88 languageName: node linkType: hard @@ -5314,7 +5336,7 @@ __metadata: languageName: node linkType: hard -"fs-minipass@npm:^2.0.0, fs-minipass@npm:^2.1.0": +"fs-minipass@npm:^2.0.0": version: 2.1.0 resolution: "fs-minipass@npm:2.1.0" dependencies: @@ -5323,6 +5345,15 @@ __metadata: languageName: node linkType: hard +"fs-minipass@npm:^3.0.0": + version: 3.0.2 + resolution: "fs-minipass@npm:3.0.2" + dependencies: + minipass: ^5.0.0 + checksum: e9cc0e1f2d01c6f6f62f567aee59530aba65c6c7b2ae88c5027bc34c711ebcfcfaefd0caf254afa6adfe7d1fba16bc2537508a6235196bac7276747d078aef0a + languageName: node + linkType: hard + "fs.realpath@npm:^1.0.0": version: 1.0.0 resolution: "fs.realpath@npm:1.0.0" @@ -5429,7 +5460,7 @@ __metadata: languageName: node linkType: hard -"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.0": +"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.0, get-intrinsic@npm:^1.2.1": version: 1.2.1 resolution: "get-intrinsic@npm:1.2.1" dependencies: @@ -5475,11 +5506,11 @@ __metadata: linkType: hard "get-tsconfig@npm:^4.5.0": - version: 4.6.0 - resolution: "get-tsconfig@npm:4.6.0" + version: 4.6.2 + resolution: "get-tsconfig@npm:4.6.2" dependencies: resolve-pkg-maps: ^1.0.0 - checksum: fd2589a50e21543cf416285e5c4ac605359f49209b6c2e66bb8698fac907356e060de0a681e40881f00182b6f19771377411a88adcc78fd3954732ff54f4a54d + checksum: e791e671a9b55e91efea3ca819ecd7a25beae679e31c83234bf3dd62ddd93df070c1b95ae7e29d206358ebb6408f6f79ac6d83a32a3bbd6a6d217babe23de077 languageName: node linkType: hard @@ -5522,6 +5553,21 @@ __metadata: languageName: node linkType: hard +"glob@npm:^10.2.2": + version: 10.3.3 + resolution: "glob@npm:10.3.3" + dependencies: + foreground-child: ^3.1.0 + jackspeak: ^2.0.3 + minimatch: ^9.0.1 + minipass: ^5.0.0 || ^6.0.2 || ^7.0.0 + path-scurry: ^1.10.1 + bin: + glob: dist/cjs/src/bin.js + checksum: 29190d3291f422da0cb40b77a72fc8d2c51a36524e99b8bf412548b7676a6627489528b57250429612b6eec2e6fe7826d328451d3e694a9d15e575389308ec53 + languageName: node + linkType: hard + "glob@npm:^7.1.3, glob@npm:^7.1.4": version: 7.2.3 resolution: "glob@npm:7.2.3" @@ -5536,19 +5582,6 @@ __metadata: languageName: node linkType: hard -"glob@npm:^8.0.1": - version: 8.1.0 - resolution: "glob@npm:8.1.0" - dependencies: - fs.realpath: ^1.0.0 - inflight: ^1.0.4 - inherits: 2 - minimatch: ^5.0.1 - once: ^1.3.0 - checksum: 92fbea3221a7d12075f26f0227abac435de868dd0736a17170663783296d0dd8d3d532a5672b4488a439bf5d7fb85cdd07c11185d6cd39184f0385cbdfb86a47 - languageName: node - linkType: hard - "global@npm:4.4.0, global@npm:^4.3.1, global@npm:^4.4.0, global@npm:~4.4.0": version: 4.4.0 resolution: "global@npm:4.4.0" @@ -5599,15 +5632,15 @@ __metadata: linkType: hard "globby@npm:^13.1.3": - version: 13.1.4 - resolution: "globby@npm:13.1.4" + version: 13.2.2 + resolution: "globby@npm:13.2.2" dependencies: dir-glob: ^3.0.1 - fast-glob: ^3.2.11 - ignore: ^5.2.0 + fast-glob: ^3.3.0 + ignore: ^5.2.4 merge2: ^1.4.1 slash: ^4.0.0 - checksum: e8bc13879972082d590cd1b0e27080d90d2e12fff7eeb2cee9329c29115ace14cc5b9f899e3d6beb136ba826307a727016658919a6f383e1511d698acee81741 + checksum: f3d84ced58a901b4fcc29c846983108c426631fe47e94872868b65565495f7bee7b3defd68923bd480582771fd4bbe819217803a164a618ad76f1d22f666f41e languageName: node linkType: hard @@ -5672,13 +5705,6 @@ __metadata: languageName: node linkType: hard -"grapheme-splitter@npm:^1.0.4": - version: 1.0.4 - resolution: "grapheme-splitter@npm:1.0.4" - checksum: 0c22ec54dee1b05cd480f78cf14f732cb5b108edc073572c4ec205df4cd63f30f8db8025afc5debc8835a8ddeacf648a1c7992fe3dcd6ad38f9a476d84906620 - languageName: node - linkType: hard - "graphemer@npm:^1.4.0": version: 1.4.0 resolution: "graphemer@npm:1.4.0" @@ -5696,8 +5722,8 @@ __metadata: linkType: hard "happy-dom@npm:^10.0.0": - version: 10.0.3 - resolution: "happy-dom@npm:10.0.3" + version: 10.5.2 + resolution: "happy-dom@npm:10.5.2" dependencies: css.escape: ^1.5.1 entities: ^4.5.0 @@ -5705,7 +5731,7 @@ __metadata: webidl-conversions: ^7.0.0 whatwg-encoding: ^2.0.0 whatwg-mimetype: ^3.0.0 - checksum: 75e3e939263736f7224a81479fdf02745613583cd57ddad6658f3f80fd20275ea5bf54438fe425d0d036cdf6cb71e65cda11a090923a34d05e0497cd1962a2da + checksum: 5fa61c2367c7396da9e379e1b091d666dffa16336cfe95c058af3e3d7ada45a5c68746cee6cba524dc15a1bea6a7cdcd235be0e06f4f44ac0627ca0c08edf59b languageName: node linkType: hard @@ -5806,7 +5832,7 @@ __metadata: "@mantine/modals": ^6.0.0 "@mantine/next": ^6.0.0 "@mantine/notifications": ^6.0.0 - "@next-auth/prisma-adapter": ^1.0.7 + "@next-auth/prisma-adapter": ^1.0.5 "@next/bundle-analyzer": ^13.0.0 "@next/eslint-plugin-next": ^13.4.5 "@nivo/core": ^0.83.0 @@ -5862,7 +5888,7 @@ __metadata: i18next: ^22.5.1 js-file-download: ^0.4.12 next: 13.4.10 - next-auth: ^4.22.3 + next-auth: ^4.21.0 next-i18next: ^13.0.0 node-mocks-http: ^1.12.2 nzbget-api: ^0.0.3 @@ -5965,7 +5991,7 @@ __metadata: languageName: node linkType: hard -"http-cache-semantics@npm:^4.0.0, http-cache-semantics@npm:^4.1.0, http-cache-semantics@npm:^4.1.1": +"http-cache-semantics@npm:^4.0.0, http-cache-semantics@npm:^4.1.1": version: 4.1.1 resolution: "http-cache-semantics@npm:4.1.1" checksum: 83ac0bc60b17a3a36f9953e7be55e5c8f41acc61b22583060e8dedc9dd5e3607c823a88d0926f9150e571f90946835c7fe150732801010845c72cd8bbff1a236 @@ -6037,9 +6063,9 @@ __metadata: linkType: hard "i18next-fs-backend@npm:^2.1.1": - version: 2.1.3 - resolution: "i18next-fs-backend@npm:2.1.3" - checksum: df3586f0958b5967674fa46aa7a579276156328b3f25e744c125b98bbeb8330c7a18a513a6224841e5732860c7bd54cb4a04b8d5a9eb521046dc37c254b67349 + version: 2.1.5 + resolution: "i18next-fs-backend@npm:2.1.5" + checksum: 4607e879de8195ff0bf11bdb2367f1c45f947160404b64f52be7e438ae27288e0d11a9b53a4bde2d75a77ac7f3255aa531a4381870e278e72f85ead222ffed31 languageName: node linkType: hard @@ -6076,9 +6102,9 @@ __metadata: linkType: hard "immutable@npm:^4.0.0": - version: 4.3.0 - resolution: "immutable@npm:4.3.0" - checksum: bbd7ea99e2752e053323543d6ff1cc71a4b4614fa6121f321ca766db2bd2092f3f1e0a90784c5431350b7344a4f792fa002eac227062d59b9377b6c09063b58b + version: 4.3.1 + resolution: "immutable@npm:4.3.1" + checksum: a3a5ba29bd43f3f9a2e4d599763d7455d11a0ea57e50bf43f2836672fc80003e90d69f2a4f5b589f1f3d6986faf97f08ce1e253583740dd33c00adebab88b217 languageName: node linkType: hard @@ -6113,13 +6139,6 @@ __metadata: languageName: node linkType: hard -"infer-owner@npm:^1.0.4": - version: 1.0.4 - resolution: "infer-owner@npm:1.0.4" - checksum: 181e732764e4a0611576466b4b87dac338972b839920b2a8cde43642e4ed6bd54dc1fb0b40874728f2a2df9a1b097b8ff83b56d5f8f8e3927f837fdcb47d8a89 - languageName: node - linkType: hard - "inflight@npm:^1.0.4": version: 1.0.6 resolution: "inflight@npm:1.0.6" @@ -6241,7 +6260,7 @@ __metadata: languageName: node linkType: hard -"is-core-module@npm:^2.11.0, is-core-module@npm:^2.12.0, is-core-module@npm:^2.9.0": +"is-core-module@npm:^2.11.0, is-core-module@npm:^2.12.0, is-core-module@npm:^2.12.1, is-core-module@npm:^2.9.0": version: 2.12.1 resolution: "is-core-module@npm:2.12.1" dependencies: @@ -6428,15 +6447,11 @@ __metadata: linkType: hard "is-typed-array@npm:^1.1.10, is-typed-array@npm:^1.1.9": - version: 1.1.10 - resolution: "is-typed-array@npm:1.1.10" + version: 1.1.12 + resolution: "is-typed-array@npm:1.1.12" dependencies: - available-typed-arrays: ^1.0.5 - call-bind: ^1.0.2 - for-each: ^0.3.3 - gopd: ^1.0.1 - has-tostringtag: ^1.0.0 - checksum: aac6ecb59d4c56a1cdeb69b1f129154ef462bbffe434cb8a8235ca89b42f258b7ae94073c41b3cb7bce37f6a1733ad4499f07882d5d5093a7ba84dfc4ebb8017 + which-typed-array: ^1.1.11 + checksum: 4c89c4a3be07186caddadf92197b17fda663a9d259ea0d44a85f171558270d36059d1c386d34a12cba22dfade5aba497ce22778e866adc9406098c8fc4771796 languageName: node linkType: hard @@ -6518,23 +6533,36 @@ __metadata: linkType: hard "istanbul-lib-report@npm:^3.0.0": - version: 3.0.0 - resolution: "istanbul-lib-report@npm:3.0.0" + version: 3.0.1 + resolution: "istanbul-lib-report@npm:3.0.1" dependencies: istanbul-lib-coverage: ^3.0.0 - make-dir: ^3.0.0 + make-dir: ^4.0.0 supports-color: ^7.1.0 - checksum: 3f29eb3f53c59b987386e07fe772d24c7f58c6897f34c9d7a296f4000de7ae3de9eb95c3de3df91dc65b134c84dee35c54eee572a56243e8907c48064e34ff1b + checksum: fd17a1b879e7faf9bb1dc8f80b2a16e9f5b7b8498fe6ed580a618c34df0bfe53d2abd35bf8a0a00e628fb7405462576427c7df20bbe4148d19c14b431c974b21 languageName: node linkType: hard "istanbul-reports@npm:^3.1.4": - version: 3.1.5 - resolution: "istanbul-reports@npm:3.1.5" + version: 3.1.6 + resolution: "istanbul-reports@npm:3.1.6" dependencies: html-escaper: ^2.0.0 istanbul-lib-report: ^3.0.0 - checksum: 7867228f83ed39477b188ea07e7ccb9b4f5320b6f73d1db93a0981b7414fa4ef72d3f80c4692c442f90fc250d9406e71d8d7ab65bb615cb334e6292b73192b89 + checksum: 44c4c0582f287f02341e9720997f9e82c071627e1e862895745d5f52ec72c9b9f38e1d12370015d2a71dcead794f34c7732aaef3fab80a24bc617a21c3d911d6 + languageName: node + linkType: hard + +"jackspeak@npm:^2.0.3": + version: 2.2.2 + resolution: "jackspeak@npm:2.2.2" + dependencies: + "@isaacs/cliui": ^8.0.2 + "@pkgjs/parseargs": ^0.11.0 + dependenciesMeta: + "@pkgjs/parseargs": + optional: true + checksum: 7b1468dd910afc00642db87448f24b062346570b8b47531409aa9012bcb95fdf7ec2b1c48edbb8b57a938c08391f8cc01b5034fc335aa3a2e74dbcc0ee5c555a languageName: node linkType: hard @@ -6545,15 +6573,15 @@ __metadata: languageName: node linkType: hard -"jest-diff@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-diff@npm:29.5.0" +"jest-diff@npm:^29.6.2": + version: 29.6.2 + resolution: "jest-diff@npm:29.6.2" dependencies: chalk: ^4.0.0 diff-sequences: ^29.4.3 jest-get-type: ^29.4.3 - pretty-format: ^29.5.0 - checksum: dfd0f4a299b5d127779c76b40106c37854c89c3e0785098c717d52822d6620d227f6234c3a9291df204d619e799e3654159213bf93220f79c8e92a55475a3d39 + pretty-format: ^29.6.2 + checksum: 0effd66a0c23f8c139ebf7ca99ed30b479b86fff66f19ad4869f130aaf7ae6a24ca1533f697b7e4930cbe2ddffc85387723fcca673501c653fb77a38f538e959 languageName: node linkType: hard @@ -6564,46 +6592,46 @@ __metadata: languageName: node linkType: hard -"jest-matcher-utils@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-matcher-utils@npm:29.5.0" +"jest-matcher-utils@npm:^29.6.2": + version: 29.6.2 + resolution: "jest-matcher-utils@npm:29.6.2" dependencies: chalk: ^4.0.0 - jest-diff: ^29.5.0 + jest-diff: ^29.6.2 jest-get-type: ^29.4.3 - pretty-format: ^29.5.0 - checksum: 1d3e8c746e484a58ce194e3aad152eff21fd0896e8b8bf3d4ab1a4e2cbfed95fb143646f4ad9fdf6e42212b9e8fc033268b58e011b044a9929df45485deb5ac9 + pretty-format: ^29.6.2 + checksum: 3e1b65dd30d05f75fe56dc45fbe4135aec2ff96a3d1e21afbf6a66f3a45a7e29cd0fd37cf80b9564e0381d6205833f77ccaf766c6f7e1aad6b7924d117be504e languageName: node linkType: hard -"jest-message-util@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-message-util@npm:29.5.0" +"jest-message-util@npm:^29.6.2": + version: 29.6.2 + resolution: "jest-message-util@npm:29.6.2" dependencies: "@babel/code-frame": ^7.12.13 - "@jest/types": ^29.5.0 + "@jest/types": ^29.6.1 "@types/stack-utils": ^2.0.0 chalk: ^4.0.0 graceful-fs: ^4.2.9 micromatch: ^4.0.4 - pretty-format: ^29.5.0 + pretty-format: ^29.6.2 slash: ^3.0.0 stack-utils: ^2.0.3 - checksum: daddece6bbf846eb6a2ab9be9f2446e54085bef4e5cecd13d2a538fa9c01cb89d38e564c6b74fd8e12d37ed9eface8a362240ae9f21d68b214590631e7a0d8bf + checksum: e8e3c8d2301e2ca4038ed6df8cbba7fedc6949d1ede4c0e3f1f44f53afb56d77eb35983fa460140d0eadeab99a5f3ae04b703fe77cd7b316b40b361228b5aa1a languageName: node linkType: hard -"jest-util@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-util@npm:29.5.0" +"jest-util@npm:^29.6.2": + version: 29.6.2 + resolution: "jest-util@npm:29.6.2" dependencies: - "@jest/types": ^29.5.0 + "@jest/types": ^29.6.1 "@types/node": "*" chalk: ^4.0.0 ci-info: ^3.2.0 graceful-fs: ^4.2.9 picomatch: ^2.2.3 - checksum: fd9212950d34d2ecad8c990dda0d8ea59a8a554b0c188b53ea5d6c4a0829a64f2e1d49e6e85e812014933d17426d7136da4785f9cf76fff1799de51b88bc85d3 + checksum: 8aedc0c80083d0cabd6c6c4f04dea1cbcac609fd7bc3b1fc05a3999291bd6e63dd52b0c806f9378d5cae28eff5a6191709a4987861001293f8d03e53984adca4 languageName: node linkType: hard @@ -6704,12 +6732,14 @@ __metadata: linkType: hard "jsx-ast-utils@npm:^2.4.1 || ^3.0.0, jsx-ast-utils@npm:^3.3.3": - version: 3.3.3 - resolution: "jsx-ast-utils@npm:3.3.3" + version: 3.3.4 + resolution: "jsx-ast-utils@npm:3.3.4" dependencies: - array-includes: ^3.1.5 - object.assign: ^4.1.3 - checksum: a2ed78cac49a0f0c4be8b1eafe3c5257a1411341d8e7f1ac740debae003de04e5f6372bfcfbd9d082e954ffd99aac85bcda85b7c6bc11609992483f4cdc0f745 + array-includes: ^3.1.6 + array.prototype.flat: ^1.3.1 + object.assign: ^4.1.4 + object.values: ^1.1.6 + checksum: a6a00d324e38f0d47e04f973d79670248a663422a4dccdc02efd6f1caf1c00042fb0aafcff1023707c85dea6f013d435b90db67c1c6841bf345628f0a720d8b3 languageName: node linkType: hard @@ -6729,12 +6759,12 @@ __metadata: languageName: node linkType: hard -"keyv@npm:^4.0.0, keyv@npm:^4.5.2": - version: 4.5.2 - resolution: "keyv@npm:4.5.2" +"keyv@npm:^4.0.0, keyv@npm:^4.5.3": + version: 4.5.3 + resolution: "keyv@npm:4.5.3" dependencies: json-buffer: 3.0.1 - checksum: 13ad58303acd2261c0d4831b4658451603fd159e61daea2121fcb15feb623e75ee328cded0572da9ca76b7b3ceaf8e614f1806c6b3af5db73c9c35a345259651 + checksum: 3ffb4d5b72b6b4b4af443bbb75ca2526b23c750fccb5ac4c267c6116888b4b65681015c2833cb20d26cf3e6e32dac6b988c77f7f022e1a571b7d90f1442257da languageName: node linkType: hard @@ -6867,6 +6897,13 @@ __metadata: languageName: node linkType: hard +"lru-cache@npm:^9.1.1 || ^10.0.0": + version: 10.0.0 + resolution: "lru-cache@npm:10.0.0" + checksum: 18f101675fe283bc09cda0ef1e3cc83781aeb8373b439f086f758d1d91b28730950db785999cd060d3c825a8571c03073e8c14512b6655af2188d623031baf50 + languageName: node + linkType: hard + "lz-string@npm:^1.5.0": version: 1.5.0 resolution: "lz-string@npm:1.5.0" @@ -6888,15 +6925,15 @@ __metadata: linkType: hard "magic-string@npm:^0.30.1": - version: 0.30.1 - resolution: "magic-string@npm:0.30.1" + version: 0.30.2 + resolution: "magic-string@npm:0.30.2" dependencies: "@jridgewell/sourcemap-codec": ^1.4.15 - checksum: 7bc7e4493e32a77068f3753bf8652d4ab44142122eb7fb9fa871af83bef2cd2c57518a6769701cd5d0379bd624a13bc8c72ca25ac5655b27e5a61adf1fd38db2 + checksum: c0bbb9b27b2772e6bfaa5d0f6452d47c462d588ae7c43fbaac062b07836d3ec0140fcdd42a57aa53ed990abafcdd0fc17907813921b5df04eccf43e67674bc57 languageName: node linkType: hard -"make-dir@npm:^3.0.0, make-dir@npm:^3.1.0": +"make-dir@npm:^3.1.0": version: 3.1.0 resolution: "make-dir@npm:3.1.0" dependencies: @@ -6905,6 +6942,15 @@ __metadata: languageName: node linkType: hard +"make-dir@npm:^4.0.0": + version: 4.0.0 + resolution: "make-dir@npm:4.0.0" + dependencies: + semver: ^7.5.3 + checksum: bf0731a2dd3aab4db6f3de1585cea0b746bb73eb5a02e3d8d72757e376e64e6ada190b1eddcde5b2f24a81b688a9897efd5018737d05e02e2a671dda9cff8a8a + languageName: node + linkType: hard + "make-error@npm:^1.1.1": version: 1.3.6 resolution: "make-error@npm:1.3.6" @@ -6912,27 +6958,26 @@ __metadata: languageName: node linkType: hard -"make-fetch-happen@npm:^10.0.3": - version: 10.2.1 - resolution: "make-fetch-happen@npm:10.2.1" +"make-fetch-happen@npm:^11.0.3": + version: 11.1.1 + resolution: "make-fetch-happen@npm:11.1.1" dependencies: agentkeepalive: ^4.2.1 - cacache: ^16.1.0 - http-cache-semantics: ^4.1.0 + cacache: ^17.0.0 + http-cache-semantics: ^4.1.1 http-proxy-agent: ^5.0.0 https-proxy-agent: ^5.0.0 is-lambda: ^1.0.1 lru-cache: ^7.7.1 - minipass: ^3.1.6 - minipass-collect: ^1.0.2 - minipass-fetch: ^2.0.3 + minipass: ^5.0.0 + minipass-fetch: ^3.0.0 minipass-flush: ^1.0.5 minipass-pipeline: ^1.2.4 negotiator: ^0.6.3 promise-retry: ^2.0.1 socks-proxy-agent: ^7.0.0 - ssri: ^9.0.0 - checksum: 2332eb9a8ec96f1ffeeea56ccefabcb4193693597b132cd110734d50f2928842e22b84cfa1508e921b8385cdfd06dda9ad68645fed62b50fff629a580f5fb72c + ssri: ^10.0.0 + checksum: 7268bf274a0f6dcf0343829489a4506603ff34bd0649c12058753900b0eb29191dce5dba12680719a5d0a983d3e57810f594a12f3c18494e93a1fbc6348a4540 languageName: node linkType: hard @@ -7075,12 +7120,12 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^5.0.1": - version: 5.1.6 - resolution: "minimatch@npm:5.1.6" +"minimatch@npm:^9.0.1": + version: 9.0.3 + resolution: "minimatch@npm:9.0.3" dependencies: brace-expansion: ^2.0.1 - checksum: 7564208ef81d7065a370f788d337cd80a689e981042cb9a1d0e6580b6c6a8c9279eba80010516e258835a988363f99f54a6f711a315089b8b42694f5da9d0d77 + checksum: 253487976bf485b612f16bf57463520a14f512662e592e95c571afdab1442a6a6864b6c88f248ce6fc4ff0b6de04ac7aa6c8bb51e868e99d1d65eb0658a708b5 languageName: node linkType: hard @@ -7100,18 +7145,18 @@ __metadata: languageName: node linkType: hard -"minipass-fetch@npm:^2.0.3": - version: 2.1.2 - resolution: "minipass-fetch@npm:2.1.2" +"minipass-fetch@npm:^3.0.0": + version: 3.0.3 + resolution: "minipass-fetch@npm:3.0.3" dependencies: encoding: ^0.1.13 - minipass: ^3.1.6 + minipass: ^5.0.0 minipass-sized: ^1.0.3 minizlib: ^2.1.2 dependenciesMeta: encoding: optional: true - checksum: 3f216be79164e915fc91210cea1850e488793c740534985da017a4cbc7a5ff50506956d0f73bb0cb60e4fe91be08b6b61ef35101706d3ef5da2c8709b5f08f91 + checksum: af5ab2552a16fcf505d35fd7ffb84b57f4a0eeb269e6e1d9a2a75824dda48b36e527083250b7cca4a4def21d9544e2ade441e4730e233c0bc2133f6abda31e18 languageName: node linkType: hard @@ -7142,7 +7187,7 @@ __metadata: languageName: node linkType: hard -"minipass@npm:^3.0.0, minipass@npm:^3.1.1, minipass@npm:^3.1.6": +"minipass@npm:^3.0.0": version: 3.3.6 resolution: "minipass@npm:3.3.6" dependencies: @@ -7158,6 +7203,13 @@ __metadata: languageName: node linkType: hard +"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0": + version: 7.0.2 + resolution: "minipass@npm:7.0.2" + checksum: 46776de732eb7cef2c7404a15fb28c41f5c54a22be50d47b03c605bf21f5c18d61a173c0a20b49a97e7a65f78d887245066410642551e45fffe04e9ac9e325bc + languageName: node + linkType: hard + "minizlib@npm:^2.1.1, minizlib@npm:^2.1.2": version: 2.1.2 resolution: "minizlib@npm:2.1.2" @@ -7175,7 +7227,7 @@ __metadata: languageName: node linkType: hard -"mkdirp@npm:^1.0.3, mkdirp@npm:^1.0.4": +"mkdirp@npm:^1.0.3": version: 1.0.4 resolution: "mkdirp@npm:1.0.4" bin: @@ -7184,19 +7236,7 @@ __metadata: languageName: node linkType: hard -"mlly@npm:^1.2.0": - version: 1.3.0 - resolution: "mlly@npm:1.3.0" - dependencies: - acorn: ^8.8.2 - pathe: ^1.1.0 - pkg-types: ^1.0.3 - ufo: ^1.1.2 - checksum: aea2a99131b1a1f02a733219317b6466156e150473e0a2f490802eaf2dc66940a21bb68e0ddd5c003360263e674e7dd0bd02da6520c740e6d16fa0edf5efa46e - languageName: node - linkType: hard - -"mlly@npm:^1.4.0": +"mlly@npm:^1.2.0, mlly@npm:^1.4.0": version: 1.4.0 resolution: "mlly@npm:1.4.0" dependencies: @@ -7304,7 +7344,7 @@ __metadata: languageName: node linkType: hard -"next-auth@npm:^4.22.3": +"next-auth@npm:^4.21.0": version: 4.22.3 resolution: "next-auth@npm:4.22.3" dependencies: @@ -7422,21 +7462,7 @@ __metadata: languageName: node linkType: hard -"node-fetch@npm:^2.6.11": - version: 2.6.11 - resolution: "node-fetch@npm:2.6.11" - dependencies: - whatwg-url: ^5.0.0 - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true - checksum: 249d0666a9497553384d46b5ab296ba223521ac88fed4d8a17d6ee6c2efb0fc890f3e8091cafe7f9fba8151a5b8d925db2671543b3409a56c3cd522b468b47b3 - languageName: node - linkType: hard - -"node-fetch@npm:^2.6.7": +"node-fetch@npm:^2.6.12, node-fetch@npm:^2.6.7": version: 2.6.12 resolution: "node-fetch@npm:2.6.12" dependencies: @@ -7451,13 +7477,14 @@ __metadata: linkType: hard "node-gyp@npm:latest": - version: 9.3.1 - resolution: "node-gyp@npm:9.3.1" + version: 9.4.0 + resolution: "node-gyp@npm:9.4.0" dependencies: env-paths: ^2.2.0 + exponential-backoff: ^3.1.1 glob: ^7.1.4 graceful-fs: ^4.2.6 - make-fetch-happen: ^10.0.3 + make-fetch-happen: ^11.0.3 nopt: ^6.0.0 npmlog: ^6.0.0 rimraf: ^3.0.2 @@ -7466,7 +7493,7 @@ __metadata: which: ^2.0.2 bin: node-gyp: bin/node-gyp.js - checksum: b860e9976fa645ca0789c69e25387401b4396b93c8375489b5151a6c55cf2640a3b6183c212b38625ef7c508994930b72198338e3d09b9d7ade5acc4aaf51ea7 + checksum: 78b404e2e0639d64e145845f7f5a3cb20c0520cdaf6dda2f6e025e9b644077202ea7de1232396ba5bde3fee84cdc79604feebe6ba3ec84d464c85d407bb5da99 languageName: node linkType: hard @@ -7496,9 +7523,9 @@ __metadata: linkType: hard "node-releases@npm:^2.0.12": - version: 2.0.12 - resolution: "node-releases@npm:2.0.12" - checksum: b8c56db82c4642a0f443332b331a4396dae452a2ac5a65c8dbd93ef89ecb2fbb0da9d42ac5366d4764973febadca816cf7587dad492dce18d2a6b2af59cda260 + version: 2.0.13 + resolution: "node-releases@npm:2.0.13" + checksum: 17ec8f315dba62710cae71a8dad3cd0288ba943d2ece43504b3b1aa8625bf138637798ab470b1d9035b0545996f63000a8a926e0f6d35d0996424f8b6d36dda3 languageName: node linkType: hard @@ -7650,7 +7677,7 @@ __metadata: languageName: node linkType: hard -"object.assign@npm:^4.1.3, object.assign@npm:^4.1.4": +"object.assign@npm:^4.1.4": version: 4.1.4 resolution: "object.assign@npm:4.1.4" dependencies: @@ -7684,6 +7711,18 @@ __metadata: languageName: node linkType: hard +"object.groupby@npm:^1.0.0": + version: 1.0.0 + resolution: "object.groupby@npm:1.0.0" + dependencies: + call-bind: ^1.0.2 + define-properties: ^1.2.0 + es-abstract: ^1.21.2 + get-intrinsic: ^1.2.1 + checksum: 64b00b287d57580111c958e7ff375c9b61811fa356f2cf0d35372d43cab61965701f00fac66c19fd8f49c4dfa28744bee6822379c69a73648ad03e09fcdeae70 + languageName: node + linkType: hard + "object.hasown@npm:^1.1.2": version: 1.1.2 resolution: "object.hasown@npm:1.1.2" @@ -7899,6 +7938,16 @@ __metadata: languageName: node linkType: hard +"path-scurry@npm:^1.10.1": + version: 1.10.1 + resolution: "path-scurry@npm:1.10.1" + dependencies: + lru-cache: ^9.1.1 || ^10.0.0 + minipass: ^5.0.0 || ^6.0.2 || ^7.0.0 + checksum: e2557cff3a8fb8bc07afdd6ab163a92587884f9969b05bbbaf6fe7379348bfb09af9ed292af12ed32398b15fb443e81692047b786d1eeb6d898a51eb17ed7d90 + languageName: node + linkType: hard + "path-type@npm:^4.0.0": version: 4.0.0 resolution: "path-type@npm:4.0.0" @@ -7967,14 +8016,14 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.4.23": - version: 8.4.24 - resolution: "postcss@npm:8.4.24" +"postcss@npm:^8.4.26": + version: 8.4.27 + resolution: "postcss@npm:8.4.27" dependencies: nanoid: ^3.3.6 picocolors: ^1.0.0 source-map-js: ^1.0.2 - checksum: 814e2126dacfea313588eda09cc99a9b4c26ec55c059188aa7a916d20d26d483483106dc5ff9e560731b59f45c5bb91b945dfadc670aed875cc90ddbbf4e787d + checksum: 1cdd0c298849df6cd65f7e646a3ba36870a37b65f55fd59d1a165539c263e9b4872a402bf4ed1ca1bc31f58b68b2835545e33ea1a23b161a1f8aa6d5ded81e78 languageName: node linkType: hard @@ -8023,14 +8072,14 @@ __metadata: languageName: node linkType: hard -"pretty-format@npm:^29.0.0, pretty-format@npm:^29.5.0": - version: 29.5.0 - resolution: "pretty-format@npm:29.5.0" +"pretty-format@npm:^29.0.0, pretty-format@npm:^29.5.0, pretty-format@npm:^29.6.2": + version: 29.6.2 + resolution: "pretty-format@npm:29.6.2" dependencies: - "@jest/schemas": ^29.4.3 + "@jest/schemas": ^29.6.0 ansi-styles: ^5.0.0 react-is: ^18.0.0 - checksum: 4065356b558e6db25b4d41a01efb386935a6c06a0c9c104ef5ce59f2f476b8210edb8b3949b386e60ada0a6dc5ebcb2e6ccddc8c64dfd1a9943c3c3a9e7eaf89 + checksum: a0f972a44f959023c0df9cdfe9eed7540264d7f7ddf74667db8a5294444d5aa153fd47d20327df10ae86964e2ceec10e46ea06b1a5c9c12e02348b78c952c9fc languageName: node linkType: hard @@ -8073,13 +8122,6 @@ __metadata: languageName: node linkType: hard -"promise-inflight@npm:^1.0.1": - version: 1.0.1 - resolution: "promise-inflight@npm:1.0.1" - checksum: 22749483091d2c594261517f4f80e05226d4d5ecc1fc917e1886929da56e22b5718b7f2a75f3807e7a7d471bc3be2907fe92e6e8f373ddf5c64bae35b5af3981 - languageName: node - linkType: hard - "promise-retry@npm:^2.0.1": version: 2.0.1 resolution: "promise-retry@npm:2.0.1" @@ -8477,7 +8519,7 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.19.0, resolve@npm:^1.22.1": +"resolve@npm:^1.19.0, resolve@npm:^1.22.1, resolve@npm:^1.22.3": version: 1.22.3 resolution: "resolve@npm:1.22.3" dependencies: @@ -8503,7 +8545,7 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@^1.19.0#~builtin, resolve@patch:resolve@^1.22.1#~builtin": +"resolve@patch:resolve@^1.19.0#~builtin, resolve@patch:resolve@^1.22.1#~builtin, resolve@patch:resolve@^1.22.3#~builtin": version: 1.22.3 resolution: "resolve@patch:resolve@npm%3A1.22.3#~builtin::version=1.22.3&hash=c3c19d" dependencies: @@ -8572,9 +8614,9 @@ __metadata: languageName: node linkType: hard -"rollup@npm:^3.21.0": - version: 3.24.0 - resolution: "rollup@npm:3.24.0" +"rollup@npm:^3.25.2": + version: 3.27.0 + resolution: "rollup@npm:3.27.0" dependencies: fsevents: ~2.3.2 dependenciesMeta: @@ -8582,7 +8624,7 @@ __metadata: optional: true bin: rollup: dist/bin/rollup - checksum: 373d0062a79cfce3583d4f6b7ab8ac9aa3201a9af1fa20b24f61a4ddea95a45974c4a8baed3087cb4e7bfc34a9dcd6774b7a635eb071ba52f97f51a59e860d6e + checksum: f60c2c288d039dc14e1f6e7fd673b7fcb11928b5a781675791b37a741f63b7af110fc5d040d60d603175b6e03ff978bed83db018dd2ac542ef809fe1a5b32dae languageName: node linkType: hard @@ -8633,6 +8675,18 @@ __metadata: languageName: node linkType: hard +"safe-array-concat@npm:^1.0.0": + version: 1.0.0 + resolution: "safe-array-concat@npm:1.0.0" + dependencies: + call-bind: ^1.0.2 + get-intrinsic: ^1.2.0 + has-symbols: ^1.0.3 + isarray: ^2.0.5 + checksum: f43cb98fe3b566327d0c09284de2b15fb85ae964a89495c1b1a5d50c7c8ed484190f4e5e71aacc167e16231940079b326f2c0807aea633d47cc7322f40a6b57f + languageName: node + linkType: hard + "safe-buffer@npm:5.2.1, safe-buffer@npm:~5.2.0": version: 5.2.1 resolution: "safe-buffer@npm:5.2.1" @@ -8675,15 +8729,15 @@ __metadata: linkType: hard "sass@npm:^1.56.1": - version: 1.63.6 - resolution: "sass@npm:1.63.6" + version: 1.64.1 + resolution: "sass@npm:1.64.1" dependencies: chokidar: ">=3.0.0 <4.0.0" immutable: ^4.0.0 source-map-js: ">=0.6.2 <2.0.0" bin: sass: sass.js - checksum: 3372319904658eeafaf78a09a6fcb3368a68e6d76fe3c43c2d009f4f72e475ab22b82ef483ef5c00fcda3ab00066846c0bd88c36b42771b855f6ab80c7eda541 + checksum: e908f96f3d5fa5869e2f2aec97548c93d6ef390680af89870fcae8bdbaee2392ac650fbeae8d2ef8e4c99cb9f81e6b3624e1cb659af6d6e746332a22233b5ad8 languageName: node linkType: hard @@ -8703,7 +8757,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^6.0.0, semver@npm:^6.3.0": +"semver@npm:^6.0.0, semver@npm:^6.3.0, semver@npm:^6.3.1": version: 6.3.1 resolution: "semver@npm:6.3.1" bin: @@ -8712,7 +8766,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.5.0": +"semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.5.3, semver@npm:^7.5.4": version: 7.5.4 resolution: "semver@npm:7.5.4" dependencies: @@ -8771,6 +8825,13 @@ __metadata: languageName: node linkType: hard +"signal-exit@npm:^4.0.1": + version: 4.0.2 + resolution: "signal-exit@npm:4.0.2" + checksum: 41f5928431cc6e91087bf0343db786a6313dd7c6fd7e551dbc141c95bb5fb26663444fd9df8ea47c5d7fc202f60aa7468c3162a9365cbb0615fc5e1b1328fe31 + languageName: node + linkType: hard + "sirv@npm:^1.0.7": version: 1.0.19 resolution: "sirv@npm:1.0.19" @@ -8857,28 +8918,28 @@ __metadata: linkType: hard "ssh2@npm:^1.11.0": - version: 1.13.0 - resolution: "ssh2@npm:1.13.0" + version: 1.14.0 + resolution: "ssh2@npm:1.14.0" dependencies: asn1: ^0.2.6 bcrypt-pbkdf: ^1.0.2 - cpu-features: ~0.0.7 + cpu-features: ~0.0.8 nan: ^2.17.0 dependenciesMeta: cpu-features: optional: true nan: optional: true - checksum: 56df3eb9d0c579230001af99b8b83cec12c3cc393720f27af59eefbe542b453a3190ecd1ab56fbb8b71ae3e5381b39387dc3587bc7b413c495ccb74e62f7689f + checksum: c583527950312716f1b620d5120e3c3e241f8cc221f19fc88fd3d561c6020c1009532438f2177a2e706223d91842deff137d93e00832b7b9016593da9a00fb89 languageName: node linkType: hard -"ssri@npm:^9.0.0": - version: 9.0.1 - resolution: "ssri@npm:9.0.1" +"ssri@npm:^10.0.0": + version: 10.0.4 + resolution: "ssri@npm:10.0.4" dependencies: - minipass: ^3.1.1 - checksum: fb58f5e46b6923ae67b87ad5ef1c5ab6d427a17db0bead84570c2df3cd50b4ceb880ebdba2d60726588272890bae842a744e1ecce5bd2a2a582fccd5068309eb + minipass: ^5.0.0 + checksum: fb14da9f8a72b04eab163eb13a9dda11d5962cd2317f85457c4e0b575e9a6e0e3a6a87b5bf122c75cb36565830cd5f263fb457571bf6f1587eb5f95d095d6165 languageName: node linkType: hard @@ -8921,7 +8982,7 @@ __metadata: languageName: node linkType: hard -"string-width@npm:^1.0.2 || 2 || 3 || 4, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3": +"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^1.0.2 || 2 || 3 || 4, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3": version: 4.2.3 resolution: "string-width@npm:4.2.3" dependencies: @@ -8932,6 +8993,17 @@ __metadata: languageName: node linkType: hard +"string-width@npm:^5.0.1, string-width@npm:^5.1.2": + version: 5.1.2 + resolution: "string-width@npm:5.1.2" + dependencies: + eastasianwidth: ^0.2.0 + emoji-regex: ^9.2.2 + strip-ansi: ^7.0.1 + checksum: 7369deaa29f21dda9a438686154b62c2c5f661f8dda60449088f9f980196f7908fc39fdd1803e3e01541970287cf5deae336798337e9319a7055af89dafa7193 + languageName: node + linkType: hard + "string.prototype.matchall@npm:^4.0.8": version: 4.0.8 resolution: "string.prototype.matchall@npm:4.0.8" @@ -9006,7 +9078,7 @@ __metadata: languageName: node linkType: hard -"strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": version: 6.0.1 resolution: "strip-ansi@npm:6.0.1" dependencies: @@ -9015,6 +9087,15 @@ __metadata: languageName: node linkType: hard +"strip-ansi@npm:^7.0.1": + version: 7.1.0 + resolution: "strip-ansi@npm:7.1.0" + dependencies: + ansi-regex: ^6.0.1 + checksum: 859c73fcf27869c22a4e4d8c6acfe690064659e84bef9458aa6d13719d09ca88dcfd40cbf31fd0be63518ea1a643fe070b4827d353e09533a5b0b9fd4553d64d + languageName: node + linkType: hard + "strip-bom@npm:^3.0.0": version: 3.0.0 resolution: "strip-bom@npm:3.0.0" @@ -9103,11 +9184,11 @@ __metadata: linkType: hard "superjson@npm:^1.10.0": - version: 1.12.3 - resolution: "superjson@npm:1.12.3" + version: 1.13.1 + resolution: "superjson@npm:1.13.1" dependencies: copy-anything: ^3.0.2 - checksum: 3549cc1d03e93745632d8114f91ed1668d81a0cf4c618f8f89a1b06f426a9cd1a2879f0e79469a6a193fd19dcea9a8fecff6215d12527b98c40c67cd98f185d3 + checksum: 9c8c664a924ce097250112428805ccc8b500018b31a91042e953d955108b8481c156005d836b413940c9fa5f124a3195f55f3a518fe76510a254a59f9151a204 languageName: node linkType: hard @@ -9147,9 +9228,9 @@ __metadata: linkType: hard "tabbable@npm:^6.0.1": - version: 6.1.2 - resolution: "tabbable@npm:6.1.2" - checksum: 1e2d9af4f172a3793f491bab10263c26903c2be6a5c4ab723d69d1ba2054828d22a50add9ba7fc42735468871d40054d906fa4f0a9dc2fbd4feff875e84c1a89 + version: 6.2.0 + resolution: "tabbable@npm:6.2.0" + checksum: f8440277d223949272c74bb627a3371be21735ca9ad34c2570f7e1752bd646ccfc23a9d8b1ee65d6561243f4134f5fbbf1ad6b39ac3c4b586554accaff4a1300 languageName: node linkType: hard @@ -9359,8 +9440,8 @@ __metadata: linkType: hard "tsconfck@npm:^2.1.0": - version: 2.1.1 - resolution: "tsconfck@npm:2.1.1" + version: 2.1.2 + resolution: "tsconfck@npm:2.1.2" peerDependencies: typescript: ^4.3.5 || ^5.0.0 peerDependenciesMeta: @@ -9368,11 +9449,11 @@ __metadata: optional: true bin: tsconfck: bin/tsconfck.js - checksum: c531525f39763cbbd7e6dbf5e29f12a7ae67eb8712816c14d06a9db6cbdc9dda9ac3cd6db07ef645f8a4cdea906447ab44e2c8679e320871cf9dd598756e8c83 + checksum: 6fd2f7de012a724f6b4bf48ae76cc7dae2b59dd5cad2dc50bac58d224d4ed7d5c43c6b26e55d3e00636f426f8b5373c996523d73b7830d05f8479a9b83282192 languageName: node linkType: hard -"tsconfig-paths@npm:^3.14.1": +"tsconfig-paths@npm:^3.14.2": version: 3.14.2 resolution: "tsconfig-paths@npm:3.14.2" dependencies: @@ -9391,10 +9472,10 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.0.0, tslib@npm:^2.1.0, tslib@npm:^2.4.0, tslib@npm:^2.5.0": - version: 2.5.3 - resolution: "tslib@npm:2.5.3" - checksum: 88902b309afaf83259131c1e13da1dceb0ad1682a213143a1346a649143924d78cf3760c448b84d796938fd76127183894f8d85cbb3bf9c4fddbfcc140c0003c +"tslib@npm:^2.0.0, tslib@npm:^2.1.0, tslib@npm:^2.4.0, tslib@npm:^2.5.0, tslib@npm:^2.6.0": + version: 2.6.1 + resolution: "tslib@npm:2.6.1" + checksum: b0d176d176487905b66ae4d5856647df50e37beea7571c53b8d10ba9222c074b81f1410fb91da13debaf2cbc970663609068bdebafa844ea9d69b146527c38fe languageName: node linkType: hard @@ -9416,58 +9497,58 @@ __metadata: languageName: node linkType: hard -"turbo-darwin-64@npm:1.10.3": - version: 1.10.3 - resolution: "turbo-darwin-64@npm:1.10.3" +"turbo-darwin-64@npm:1.10.12": + version: 1.10.12 + resolution: "turbo-darwin-64@npm:1.10.12" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"turbo-darwin-arm64@npm:1.10.3": - version: 1.10.3 - resolution: "turbo-darwin-arm64@npm:1.10.3" +"turbo-darwin-arm64@npm:1.10.12": + version: 1.10.12 + resolution: "turbo-darwin-arm64@npm:1.10.12" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"turbo-linux-64@npm:1.10.3": - version: 1.10.3 - resolution: "turbo-linux-64@npm:1.10.3" +"turbo-linux-64@npm:1.10.12": + version: 1.10.12 + resolution: "turbo-linux-64@npm:1.10.12" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"turbo-linux-arm64@npm:1.10.3": - version: 1.10.3 - resolution: "turbo-linux-arm64@npm:1.10.3" +"turbo-linux-arm64@npm:1.10.12": + version: 1.10.12 + resolution: "turbo-linux-arm64@npm:1.10.12" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"turbo-windows-64@npm:1.10.3": - version: 1.10.3 - resolution: "turbo-windows-64@npm:1.10.3" +"turbo-windows-64@npm:1.10.12": + version: 1.10.12 + resolution: "turbo-windows-64@npm:1.10.12" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"turbo-windows-arm64@npm:1.10.3": - version: 1.10.3 - resolution: "turbo-windows-arm64@npm:1.10.3" +"turbo-windows-arm64@npm:1.10.12": + version: 1.10.12 + resolution: "turbo-windows-arm64@npm:1.10.12" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard "turbo@npm:latest": - version: 1.10.3 - resolution: "turbo@npm:1.10.3" + version: 1.10.12 + resolution: "turbo@npm:1.10.12" dependencies: - turbo-darwin-64: 1.10.3 - turbo-darwin-arm64: 1.10.3 - turbo-linux-64: 1.10.3 - turbo-linux-arm64: 1.10.3 - turbo-windows-64: 1.10.3 - turbo-windows-arm64: 1.10.3 + turbo-darwin-64: 1.10.12 + turbo-darwin-arm64: 1.10.12 + turbo-linux-64: 1.10.12 + turbo-linux-arm64: 1.10.12 + turbo-windows-64: 1.10.12 + turbo-windows-arm64: 1.10.12 dependenciesMeta: turbo-darwin-64: optional: true @@ -9483,7 +9564,7 @@ __metadata: optional: true bin: turbo: bin/turbo - checksum: 38a11c8f1548408e6c8576d13ed78e2b0a232577e64a1cb6623faa39437cdc28012eb388f77d53af1b1853c521000aae0467cb5d91aa169b0883be976edfbdb1 + checksum: 266b70404e149b92cd64051fcdd03c56b474ed22f6bd11cf4cf1b70f55c72f9a2aaa90109b19b95e43d68e53a6be54485f0cc0135c8d5b505db09fd126a34052 languageName: node linkType: hard @@ -9527,6 +9608,42 @@ __metadata: languageName: node linkType: hard +"typed-array-buffer@npm:^1.0.0": + version: 1.0.0 + resolution: "typed-array-buffer@npm:1.0.0" + dependencies: + call-bind: ^1.0.2 + get-intrinsic: ^1.2.1 + is-typed-array: ^1.1.10 + checksum: 3e0281c79b2a40cd97fe715db803884301993f4e8c18e8d79d75fd18f796e8cd203310fec8c7fdb5e6c09bedf0af4f6ab8b75eb3d3a85da69328f28a80456bd3 + languageName: node + linkType: hard + +"typed-array-byte-length@npm:^1.0.0": + version: 1.0.0 + resolution: "typed-array-byte-length@npm:1.0.0" + dependencies: + call-bind: ^1.0.2 + for-each: ^0.3.3 + has-proto: ^1.0.1 + is-typed-array: ^1.1.10 + checksum: b03db16458322b263d87a702ff25388293f1356326c8a678d7515767ef563ef80e1e67ce648b821ec13178dd628eb2afdc19f97001ceae7a31acf674c849af94 + languageName: node + linkType: hard + +"typed-array-byte-offset@npm:^1.0.0": + version: 1.0.0 + resolution: "typed-array-byte-offset@npm:1.0.0" + dependencies: + available-typed-arrays: ^1.0.5 + call-bind: ^1.0.2 + for-each: ^0.3.3 + has-proto: ^1.0.1 + is-typed-array: ^1.1.10 + checksum: 04f6f02d0e9a948a95fbfe0d5a70b002191fae0b8fe0fe3130a9b2336f043daf7a3dda56a31333c35a067a97e13f539949ab261ca0f3692c41603a46a94e960b + languageName: node + linkType: hard + "typed-array-length@npm:^1.0.4": version: 1.0.4 resolution: "typed-array-length@npm:1.0.4" @@ -9577,21 +9694,21 @@ __metadata: languageName: node linkType: hard -"unique-filename@npm:^2.0.0": - version: 2.0.1 - resolution: "unique-filename@npm:2.0.1" +"unique-filename@npm:^3.0.0": + version: 3.0.0 + resolution: "unique-filename@npm:3.0.0" dependencies: - unique-slug: ^3.0.0 - checksum: 807acf3381aff319086b64dc7125a9a37c09c44af7620bd4f7f3247fcd5565660ac12d8b80534dcbfd067e6fe88a67e621386dd796a8af828d1337a8420a255f + unique-slug: ^4.0.0 + checksum: 8e2f59b356cb2e54aab14ff98a51ac6c45781d15ceaab6d4f1c2228b780193dc70fae4463ce9e1df4479cb9d3304d7c2043a3fb905bdeca71cc7e8ce27e063df languageName: node linkType: hard -"unique-slug@npm:^3.0.0": - version: 3.0.0 - resolution: "unique-slug@npm:3.0.0" +"unique-slug@npm:^4.0.0": + version: 4.0.0 + resolution: "unique-slug@npm:4.0.0" dependencies: imurmurhash: ^0.1.4 - checksum: 49f8d915ba7f0101801b922062ee46b7953256c93ceca74303bd8e6413ae10aa7e8216556b54dc5382895e8221d04f1efaf75f945c2e4a515b4139f77aa6640c + checksum: 0884b58365af59f89739e6f71e3feacb5b1b41f2df2d842d0757933620e6de08eff347d27e9d499b43c40476cbaf7988638d3acb2ffbcb9d35fd035591adfd15 languageName: node linkType: hard @@ -9768,8 +9885,8 @@ __metadata: linkType: hard "video.js@npm:^7 || ^8, video.js@npm:^8.0.3": - version: 8.5.0 - resolution: "video.js@npm:8.5.0" + version: 8.5.1 + resolution: "video.js@npm:8.5.1" dependencies: "@babel/runtime": ^7.12.5 "@videojs/http-streaming": 3.3.1 @@ -9784,8 +9901,8 @@ __metadata: safe-json-parse: 4.0.0 videojs-contrib-quality-levels: 4.0.0 videojs-font: 4.1.0 - videojs-vtt.js: 0.15.4 - checksum: 519b7063eb3cfe21cfea8ab65c25d8ad4da2798703d831bafd4b6930a2cc2e75f990aaba5854dac797723df9d4a172729b2dbe5ba9223c9deea3cfe671ede9f3 + videojs-vtt.js: 0.15.5 + checksum: 2ee3b41282f6b1a423d93fdf90f3f0834a948514dc4b36543456643064bf22d300349c6dec4c662da88c86921a5b170cfebda987e8f2d79aff3eb4a8ea51798b languageName: node linkType: hard @@ -9807,12 +9924,12 @@ __metadata: languageName: node linkType: hard -"videojs-vtt.js@npm:0.15.4": - version: 0.15.4 - resolution: "videojs-vtt.js@npm:0.15.4" +"videojs-vtt.js@npm:0.15.5": + version: 0.15.5 + resolution: "videojs-vtt.js@npm:0.15.5" dependencies: global: ^4.3.1 - checksum: 10c6c861621d4314e7d4b60b7bef1afc60f1ac438879f6b3f22e8944d694c8e9dfc809a8187ed72f44e06c39a159044d8fa15e80695b9bf7b9bef99ea2740b70 + checksum: 2658de26830e412f7539f2674019348374d36f05fcdae548be001d596798f50c0d2e026163afdeff34286919de7aae08cb97af612925164167204b301d246241 languageName: node linkType: hard @@ -9849,16 +9966,17 @@ __metadata: linkType: hard "vite@npm:^3.0.0 || ^4.0.0": - version: 4.3.9 - resolution: "vite@npm:4.3.9" + version: 4.4.7 + resolution: "vite@npm:4.4.7" dependencies: - esbuild: ^0.17.5 + esbuild: ^0.18.10 fsevents: ~2.3.2 - postcss: ^8.4.23 - rollup: ^3.21.0 + postcss: ^8.4.26 + rollup: ^3.25.2 peerDependencies: "@types/node": ">= 14" less: "*" + lightningcss: ^1.21.0 sass: "*" stylus: "*" sugarss: "*" @@ -9871,6 +9989,8 @@ __metadata: optional: true less: optional: true + lightningcss: + optional: true sass: optional: true stylus: @@ -9881,7 +10001,7 @@ __metadata: optional: true bin: vite: bin/vite.js - checksum: 8c45a516278d1e0425fac00c0877336790f71484a851a318346a70e0d2aef9f3b9651deb2f9f002c791ceb920eda7d6a3cda753bdefd657321c99f448b02dd25 + checksum: 787c4d891da18d0a0545bee07dec73c3201979dcf2b1ea3dc13fdd2d3b9ad76d413bcc0e68502183e309007a612c1f4116adefe0093d95fbbb9cf1c1755f7e41 languageName: node linkType: hard @@ -10064,17 +10184,16 @@ __metadata: languageName: node linkType: hard -"which-typed-array@npm:^1.1.9": - version: 1.1.9 - resolution: "which-typed-array@npm:1.1.9" +"which-typed-array@npm:^1.1.10, which-typed-array@npm:^1.1.11, which-typed-array@npm:^1.1.9": + version: 1.1.11 + resolution: "which-typed-array@npm:1.1.11" dependencies: available-typed-arrays: ^1.0.5 call-bind: ^1.0.2 for-each: ^0.3.3 gopd: ^1.0.1 has-tostringtag: ^1.0.0 - is-typed-array: ^1.1.10 - checksum: fe0178ca44c57699ca2c0e657b64eaa8d2db2372a4e2851184f568f98c478ae3dc3fdb5f7e46c384487046b0cf9e23241423242b277e03e8ba3dabc7c84c98ef + checksum: 711ffc8ef891ca6597b19539075ec3e08bb9b4c2ca1f78887e3c07a977ab91ac1421940505a197758fb5939aa9524976d0a5bbcac34d07ed6faa75cedbb17206 languageName: node linkType: hard @@ -10110,7 +10229,7 @@ __metadata: languageName: node linkType: hard -"wrap-ansi@npm:^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0, wrap-ansi@npm:^7.0.0": version: 7.0.0 resolution: "wrap-ansi@npm:7.0.0" dependencies: @@ -10121,6 +10240,17 @@ __metadata: languageName: node linkType: hard +"wrap-ansi@npm:^8.1.0": + version: 8.1.0 + resolution: "wrap-ansi@npm:8.1.0" + dependencies: + ansi-styles: ^6.1.0 + string-width: ^5.0.1 + strip-ansi: ^7.0.1 + checksum: 371733296dc2d616900ce15a0049dca0ef67597d6394c57347ba334393599e800bab03c41d4d45221b6bc967b8c453ec3ae4749eff3894202d16800fdfe0e238 + languageName: node + linkType: hard + "wrappy@npm:1": version: 1.0.2 resolution: "wrappy@npm:1.0.2" From d8562e2990f2985169b052fd87c479b80f35f1d8 Mon Sep 17 00:00:00 2001 From: Meier Lukas Date: Sat, 29 Jul 2023 10:05:05 +0200 Subject: [PATCH 004/233] =?UTF-8?q?=E2=9C=A8=20Add=20working=20sign-in=20/?= =?UTF-8?q?=20sign-out,=20add=20working=20registration=20with=20token?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 5 +- data/configs/default.json | 2 +- next-i18next.config.js | 2 +- prisma/schema.prisma | 6 + .../locales/en/authentication/register.json | 20 +++ public/locales/en/common.json | 4 + public/locales/en/zod.json | 22 +++ src/components/layout/header/Header.tsx | 1 + src/components/layout/header/SettingsMenu.tsx | 26 +++- .../header/SettingsMenu/EditModeToggle.tsx | 79 ---------- src/pages/_app.tsx | 122 ++++++++------- src/pages/login.tsx | 3 +- src/pages/register.tsx | 140 ++++++++++++++++++ src/server/api/root.ts | 2 + src/server/api/routers/user.ts | 66 +++++++++ src/server/api/trpc.ts | 2 + src/tools/server/translation-namespaces.ts | 5 +- src/utils/i18n-zod-resolver.ts | 128 ++++++++++++++++ src/validations/user.ts | 18 ++- 19 files changed, 506 insertions(+), 147 deletions(-) create mode 100644 public/locales/en/authentication/register.json create mode 100644 public/locales/en/zod.json delete mode 100644 src/components/layout/header/SettingsMenu/EditModeToggle.tsx create mode 100644 src/pages/register.tsx create mode 100644 src/server/api/routers/user.ts create mode 100644 src/utils/i18n-zod-resolver.ts diff --git a/.gitignore b/.gitignore index a7129a95d..4f96eb93c 100644 --- a/.gitignore +++ b/.gitignore @@ -55,4 +55,7 @@ data/configs #Languages other than 'en' public/locales/* -!public/locales/en \ No newline at end of file +!public/locales/en + +#database +prisma/db.sqlite \ No newline at end of file diff --git a/data/configs/default.json b/data/configs/default.json index 6d28530f3..f22f3d3db 100644 --- a/data/configs/default.json +++ b/data/configs/default.json @@ -390,4 +390,4 @@ "appOpacity": 100 } } -} +} \ No newline at end of file diff --git a/next-i18next.config.js b/next-i18next.config.js index eed2cdc6e..3b35467c2 100644 --- a/next-i18next.config.js +++ b/next-i18next.config.js @@ -29,7 +29,7 @@ module.exports = { 'no', 'tr', 'lv', - 'hr' + 'hr', ], localeDetection: true, diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 3e3980a96..8949aa855 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -61,3 +61,9 @@ model VerificationToken { @@unique([identifier, token]) } + +model RegistrationToken { + id String @id @default(cuid()) + token String @unique + expires DateTime +} diff --git a/public/locales/en/authentication/register.json b/public/locales/en/authentication/register.json new file mode 100644 index 000000000..9173bb76f --- /dev/null +++ b/public/locales/en/authentication/register.json @@ -0,0 +1,20 @@ +{ + "title": "Create Account", + "text": "Please define your credentials below", + "form": { + "fields": { + "username": { + "label": "Username" + }, + "password": { + "label": "Password" + }, + "passwordConfirmation": { + "label": "Confirm password" + } + }, + "buttons": { + "submit": "Register" + } + } +} \ No newline at end of file diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 4b5059ae0..e716bef39 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -35,5 +35,9 @@ "small": "small", "medium": "medium", "large": "large" + }, + "header": { + "logout": "Logout", + "sign-in": "Sign in" } } \ No newline at end of file diff --git a/public/locales/en/zod.json b/public/locales/en/zod.json new file mode 100644 index 000000000..41acbcf94 --- /dev/null +++ b/public/locales/en/zod.json @@ -0,0 +1,22 @@ +{ + "errors": { + "default": "This field is invalid", + "required": "This field is required", + "string": { + "startsWith": "This field must start with {{startsWith}}", + "endsWith": "This field must end with {{endsWith}}", + "includes": "This field must include {{includes}}" + }, + "too_small": { + "string": "This field must be at least {{minimum}} characters long", + "number": "This field must be greater than or equal to {{minimum}}" + }, + "too_big": { + "string": "This field must be at most {{minimum}} characters long", + "number": "This field must be less than or equal to {{minimum}}" + }, + "custom": { + "password_match": "Passwords must match" + } + } +} \ No newline at end of file diff --git a/src/components/layout/header/Header.tsx b/src/components/layout/header/Header.tsx index fc464ea0f..4398efc54 100644 --- a/src/components/layout/header/Header.tsx +++ b/src/components/layout/header/Header.tsx @@ -1,5 +1,6 @@ import { Box, Group, Indicator, Header as MantineHeader, createStyles } from '@mantine/core'; import { useQuery } from '@tanstack/react-query'; +import { useSession } from 'next-auth/react'; import { REPO_URL } from '../../../../data/constants'; import { useEditModeInformationStore } from '../../../hooks/useEditModeInformation'; diff --git a/src/components/layout/header/SettingsMenu.tsx b/src/components/layout/header/SettingsMenu.tsx index 60c835cf2..01cc9c34b 100644 --- a/src/components/layout/header/SettingsMenu.tsx +++ b/src/components/layout/header/SettingsMenu.tsx @@ -1,14 +1,21 @@ import { Badge, Button, Menu } from '@mantine/core'; import { useDisclosure } from '@mantine/hooks'; -import { IconInfoCircle, IconMenu2, IconSettings } from '@tabler/icons-react'; +import { + IconInfoCircle, + IconLogin, + IconLogout, + IconMenu2, + IconSettings, +} from '@tabler/icons-react'; +import { signOut, useSession } from 'next-auth/react'; import { useTranslation } from 'next-i18next'; +import Link from 'next/link'; import { useEditModeInformationStore } from '../../../hooks/useEditModeInformation'; import { AboutModal } from '../../Dashboard/Modals/AboutModal/AboutModal'; import { SettingsDrawer } from '../../Settings/SettingsDrawer'; import { useCardStyles } from '../useCardStyles'; import { ColorSchemeSwitch } from './SettingsMenu/ColorSchemeSwitch'; -import { EditModeToggle } from './SettingsMenu/EditModeToggle'; export function SettingsMenu({ newVersionAvailable }: { newVersionAvailable: string }) { const [drawerOpened, drawer] = useDisclosure(false); @@ -16,6 +23,7 @@ export function SettingsMenu({ newVersionAvailable }: { newVersionAvailable: str const [aboutModalOpened, aboutModal] = useDisclosure(false); const { classes } = useCardStyles(true); const { editModeEnabled } = useEditModeInformationStore(); + const { data: sessionData } = useSession(); return ( <> @@ -27,7 +35,6 @@ export function SettingsMenu({ newVersionAvailable }: { newVersionAvailable: str - {!editModeEnabled && ( } onClick={drawer.open}> @@ -47,6 +54,19 @@ export function SettingsMenu({ newVersionAvailable }: { newVersionAvailable: str > {t('about')} + {sessionData?.user ? ( + } onClick={() => signOut()}> + {t('header.logout')} + + ) : ( + } + component={Link} + href="/login" + > + {t('header.sign-in')} + + )} { - axios - .post('/api/configs/tryPassword', { tried: values.triedPassword, type: 'edit' }) - .then((res) => { - showNotification({ - title: 'Success', - message: 'Successfully toggled edit mode, reloading the page...', - color: 'green', - }); - setTimeout(() => { - window.location.reload(); - }, 500); - }) - .catch((_) => { - showNotification({ - title: 'Error', - message: 'Failed to toggle edit mode, please try again.', - color: 'red', - }); - }); - })} - > - - - In order to toggle edit mode, you need to enter the password you entered in the - environment variable named EDIT_MODE_PASSWORD . If it is not set, you are not - able to toggle edit mode on and off. - - - - - - ); -} - -export function EditModeToggle() { - const { editModeEnabled } = useEditModeInformationStore(); - const Icon = editModeEnabled ? IconEdit : IconEditOff; - - return ( - } - onClick={() => - openModal({ - title: 'Toggle edit mode', - centered: true, - size: 'lg', - children: , - }) - } - > - {editModeEnabled ? 'Enable edit mode' : 'Disable edit mode'} - - ); -} diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 681bdff42..9c40390da 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -9,12 +9,15 @@ import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client import Consola from 'consola'; import { getCookie } from 'cookies-next'; import { GetServerSidePropsContext } from 'next'; +import { Session } from 'next-auth'; +import { SessionProvider, getSession } from 'next-auth/react'; import { appWithTranslation } from 'next-i18next'; import { AppProps } from 'next/app'; import Head from 'next/head'; import { useEffect, useState } from 'react'; import 'video.js/dist/video-js.css'; import { env } from '~/env.js'; +import { ConfigType } from '~/types/config'; import { api } from '~/utils/api'; import nextI18nextConfig from '../../next-i18next.config.js'; @@ -36,7 +39,6 @@ import { getServiceSidePackageAttributes, } from '../tools/server/getPackageVersion'; import { theme } from '../tools/server/theme/theme'; -import { ConfigType } from '~/types/config'; function App( this: any, @@ -47,13 +49,20 @@ function App( defaultColorScheme: ColorScheme; config?: ConfigType; configName?: string; + session: Session; }> ) { const { Component, pageProps } = props; - const [primaryColor, setPrimaryColor] = useState(props.pageProps.config?.settings.customization.colors.primary || 'red'); - const [secondaryColor, setSecondaryColor] = useState(props.pageProps.config?.settings.customization.colors.secondary || 'orange'); - const [primaryShade, setPrimaryShade] = useState(props.pageProps.config?.settings.customization.colors.shade || 6); + const [primaryColor, setPrimaryColor] = useState( + props.pageProps.config?.settings.customization.colors.primary || 'red' + ); + const [secondaryColor, setSecondaryColor] = useState( + props.pageProps.config?.settings.customization.colors.secondary || 'orange' + ); + const [primaryShade, setPrimaryShade] = useState( + props.pageProps.config?.settings.customization.colors.shade || 6 + ); const colorTheme = { primaryColor, secondaryColor, @@ -97,62 +106,64 @@ function App( - - - - + + + + - - - - - - - - - - - + primaryColor, + primaryShade, + colorScheme, + }} + withGlobalStyles + withNormalizeCSS + > + + + + + + + + + + + + ); } -App.getInitialProps = ({ ctx }: { ctx: GetServerSidePropsContext }) => { +App.getInitialProps = async ({ ctx }: { ctx: GetServerSidePropsContext }) => { if (process.env.DISABLE_EDIT_MODE === 'true') { Consola.warn( 'EXPERIMENTAL: You have disabled the edit mode. Modifications are no longer possible and any requests on the API will be dropped. If you want to disable this, unset the DISABLE_EDIT_MODE environment variable. This behaviour may be removed in future versions of Homarr' @@ -163,12 +174,15 @@ App.getInitialProps = ({ ctx }: { ctx: GetServerSidePropsContext }) => { Consola.debug(`Overriding the default color scheme with ${env.DEFAULT_COLOR_SCHEME}`); } + const session = await getSession(ctx); + return { pageProps: { colorScheme: getCookie('color-scheme', ctx) || 'light', packageAttributes: getServiceSidePackageAttributes(), editModeEnabled: process.env.DISABLE_EDIT_MODE !== 'true', defaultColorScheme: env.DEFAULT_COLOR_SCHEME, + session, }, }; }; diff --git a/src/pages/login.tsx b/src/pages/login.tsx index 330c1b8ac..c885c29c2 100644 --- a/src/pages/login.tsx +++ b/src/pages/login.tsx @@ -24,7 +24,7 @@ import { signInSchema } from '~/validations/user'; import { loginNamespaces } from '../tools/server/translation-namespaces'; export default function LoginPage() { - const { t } = useTranslation('authentication/login'); + const { t } = useTranslation(['authentication/login']); const queryParams = useRouter().query as { error?: 'CredentialsSignin' | (string & {}) }; const form = useForm>({ @@ -63,7 +63,6 @@ export default function LoginPage() { /> >({ + validateInputOnChange: true, + validateInputOnBlur: true, + validate: i18nZodResolver(signUpFormSchema), + }); + + const handleSubmit = (values: z.infer) => { + const notificationId = 'register'; + showNotification({ + id: notificationId, + title: 'Registering...', + message: 'Please wait...', + loading: true, + }); + void mutateAsync( + { + ...values, + registerToken: query.token, + }, + { + onSuccess() { + updateNotification({ + id: notificationId, + title: 'Account created', + message: 'Your account has been created successfully', + color: 'teal', + icon: , + }); + router.push('/login'); + }, + onError() { + updateNotification({ + id: notificationId, + title: 'Error', + message: 'Something went wrong', + color: 'red', + icon: , + }); + }, + } + ); + }; + + return ( + + + + {t('title')} + + + + {t('text')} + + +
+ + + + + + + + + +
+
+
+ ); +} + +const queryParamsSchema = z.object({ + token: z.string(), +}); + +export const getServerSideProps: GetServerSideProps = async ({ locale, query }) => { + const result = queryParamsSchema.safeParse(query); + if (!result.success) { + return { + notFound: true, + }; + } + + const token = await prisma.registrationToken.findUnique({ + where: { + token: result.data.token, + }, + }); + + if (!token || token.expires < new Date()) { + return { + notFound: true, + }; + } + + return { + props: { + ...(await serverSideTranslations(locale ?? '', registerNamespaces)), + }, + }; +}; diff --git a/src/server/api/root.ts b/src/server/api/root.ts index ced44f718..8f261c685 100644 --- a/src/server/api/root.ts +++ b/src/server/api/root.ts @@ -13,6 +13,7 @@ import { mediaServerRouter } from './routers/media-server'; import { overseerrRouter } from './routers/overseerr'; import { rssRouter } from './routers/rss'; import { usenetRouter } from './routers/usenet/router'; +import { userRouter } from './routers/user'; import { weatherRouter } from './routers/weather'; /** @@ -23,6 +24,7 @@ import { weatherRouter } from './routers/weather'; export const rootRouter = createTRPCRouter({ app: appRouter, rss: rssRouter, + user: userRouter, config: configRouter, docker: dockerRouter, icon: iconRouter, diff --git a/src/server/api/routers/user.ts b/src/server/api/routers/user.ts new file mode 100644 index 000000000..50d859e95 --- /dev/null +++ b/src/server/api/routers/user.ts @@ -0,0 +1,66 @@ +import { TRPCError } from '@trpc/server'; +import bcrypt from 'bcrypt'; +import { z } from 'zod'; +import { hashPassword } from '~/utils/security'; +import { signUpFormSchema } from '~/validations/user'; + +import { createTRPCRouter, publicProcedure } from '../trpc'; + +export const userRouter = createTRPCRouter({ + register: publicProcedure + .input( + signUpFormSchema.and( + z.object({ + registerToken: z.string(), + }) + ) + ) + .mutation(async ({ ctx, input }) => { + const token = await ctx.prisma.registrationToken.findUnique({ + where: { + token: input.registerToken, + }, + }); + + if (!token || token.expires < new Date()) { + throw new TRPCError({ + code: 'FORBIDDEN', + message: 'Invalid registration token', + }); + } + + const existingUser = await ctx.prisma.user.findFirst({ + where: { + name: input.username, + }, + }); + + if (existingUser) { + throw new TRPCError({ + code: 'CONFLICT', + message: 'User already exists', + }); + } + + const salt = bcrypt.genSaltSync(10); + const hashedPassword = hashPassword(input.password, salt); + + const user = await ctx.prisma.user.create({ + data: { + name: input.username, + password: hashedPassword, + salt: salt, + }, + }); + await ctx.prisma.registrationToken.delete({ + where: { + id: token.id, + }, + }); + + return { + id: user.id, + name: user.name, + }; + }), +}); diff --git a/src/server/api/trpc.ts b/src/server/api/trpc.ts index 73198345b..3b83ba494 100644 --- a/src/server/api/trpc.ts +++ b/src/server/api/trpc.ts @@ -13,6 +13,7 @@ import superjson from 'superjson'; import { ZodError } from 'zod'; import { getServerAuthSession } from '../auth'; +import { prisma } from '../db'; /** * 1. CONTEXT @@ -38,6 +39,7 @@ interface CreateContextOptions { */ const createInnerTRPCContext = (opts: CreateContextOptions) => ({ session: opts.session, + prisma, }); /** diff --git a/src/tools/server/translation-namespaces.ts b/src/tools/server/translation-namespaces.ts index 96f25690e..06e4da980 100644 --- a/src/tools/server/translation-namespaces.ts +++ b/src/tools/server/translation-namespaces.ts @@ -1,5 +1,6 @@ export const dashboardNamespaces = [ 'common', + 'zod', 'layout/element-selector/selector', 'layout/modals/add-app', 'layout/modals/change-position', @@ -48,4 +49,6 @@ export const dashboardNamespaces = [ 'widgets/location', ]; -export const loginNamespaces = ['authentication/login']; +export const loginNamespaces = ['authentication/login', 'zod']; + +export const registerNamespaces = ['authentication/register', 'zod']; diff --git a/src/utils/i18n-zod-resolver.ts b/src/utils/i18n-zod-resolver.ts new file mode 100644 index 000000000..9ab3bb0bb --- /dev/null +++ b/src/utils/i18n-zod-resolver.ts @@ -0,0 +1,128 @@ +import { zodResolver } from '@mantine/form'; +import { TFunction } from 'i18next'; +import { useTranslation } from 'next-i18next'; +import { ErrorMapCtx, ZodIssueCode, ZodSchema, ZodTooBigIssue, ZodTooSmallIssue, z } from 'zod'; + +export const useI18nZodResolver = () => { + const { t } = useTranslation('zod'); + return { + i18nZodResolver: i18nZodResolver(t), + }; +}; + +const i18nZodResolver = + (t: TFunction<'zod', undefined, 'zod'>) => + >>(schema: TSchema) => { + z.setErrorMap(zodErrorMap(t)); + return zodResolver(schema); + }; + +const handleStringError = (issue: z.ZodInvalidStringIssue, ctx: ErrorMapCtx) => { + if (typeof issue.validation === 'object') { + if ('startsWith' in issue.validation) { + return { + key: 'errors.string.startsWith', + params: { + startsWith: issue.validation.startsWith, + }, + }; + } else if ('endsWith' in issue.validation) { + return { + key: 'errors.string.endsWith', + params: { + endsWith: issue.validation.endsWith, + }, + }; + } + + return { + key: 'errors.invalid_string.includes', + params: { + includes: issue.validation.includes, + }, + }; + } + + return { + message: issue.message, + }; +}; + +const handleTooSmallError = (issue: ZodTooSmallIssue, ctx: ErrorMapCtx) => { + if (issue.type !== 'string' && issue.type !== 'number') { + return { + message: issue.message, + }; + } + + return { + key: `errors.too_small.${issue.type}`, + params: { + minimum: issue.minimum, + count: issue.minimum, + }, + }; +}; + +const handleTooBigError = (issue: ZodTooBigIssue, ctx: ErrorMapCtx) => { + if (issue.type !== 'string' && issue.type !== 'number') { + return { + message: issue.message, + }; + } + + return { + key: `errors.too_big.${issue.type}`, + params: { + maximum: issue.maximum, + count: issue.maximum, + }, + }; +}; + +const handleZodError = (issue: z.ZodIssueOptionalMessage, ctx: ErrorMapCtx) => { + if (ctx.defaultError === 'Required') { + return { + key: 'errors.required', + params: {}, + }; + } + if (issue.code === ZodIssueCode.invalid_string) { + return handleStringError(issue, ctx); + } + if (issue.code === ZodIssueCode.too_small) { + return handleTooSmallError(issue, ctx); + } + if (issue.code === ZodIssueCode.too_big) { + return handleTooBigError(issue, ctx); + } + if (issue.code === ZodIssueCode.custom && issue.params?.i18n) { + return { + key: `errors.custom.${issue.params.i18n.key}`, + }; + } + + return { + message: issue.message, + }; +}; + +function zodErrorMap(t: TFunction<'zod', undefined, 'zod'>) { + return (issue: z.ZodIssueOptionalMessage, ctx: ErrorMapCtx) => { + const error = handleZodError(issue, ctx); + if ('message' in error && error.message) + return { + message: error.message ?? ctx.defaultError, + }; + return { + message: t(error.key ?? 'errors.default', error.params ?? {}), + }; + }; +} + +export type CustomErrorParams = { + i18n: { + key: string; + params?: Record; + }; +}; diff --git a/src/validations/user.ts b/src/validations/user.ts index edb59445f..4ce6c0848 100644 --- a/src/validations/user.ts +++ b/src/validations/user.ts @@ -1,12 +1,20 @@ import { z } from 'zod'; +import { CustomErrorParams } from '~/utils/i18n-zod-resolver'; export const signInSchema = z.object({ name: z.string(), password: z.string(), }); -export const signUpFormSchema = z.object({ - username: z.string(), - password: z.string().min(8), - acceptTos: z.boolean(), -}); +export const signUpFormSchema = z + .object({ + username: z.string().min(3), + password: z.string().min(8), + passwordConfirmation: z.string().min(8), + }) + .refine((data) => data.password === data.passwordConfirmation, { + params: { + i18n: { key: 'password_match' }, + } satisfies CustomErrorParams, + path: ['passwordConfirmation'], + }); From ed76afbce8aee5524d7c0b1b046d0f7e138e0fe9 Mon Sep 17 00:00:00 2001 From: Meier Lukas Date: Sat, 29 Jul 2023 11:19:40 +0200 Subject: [PATCH 005/233] =?UTF-8?q?=E2=9C=A8=20Add=20user=20settings,=20im?= =?UTF-8?q?prove=20color=20scheme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prisma/schema.prisma | 22 +++++++-- src/components/layout/header/SettingsMenu.tsx | 1 - src/hooks/use-colorscheme.ts | 27 +++++++++++ src/pages/_app.tsx | 46 +++++++++---------- src/server/api/routers/user.ts | 30 +++++++++++- src/server/api/trpc.ts | 3 ++ src/server/auth.ts | 26 ++++++++++- src/validations/user.ts | 5 ++ 8 files changed, 129 insertions(+), 31 deletions(-) create mode 100644 src/hooks/use-colorscheme.ts diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 8949aa855..ed932d718 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -42,16 +42,17 @@ model Session { } model User { - id String @id @default(cuid()) + id String @id @default(cuid()) name String? - email String? @unique + email String? @unique emailVerified DateTime? image String? password String? salt String? - isAdmin Boolean @default(false) + isAdmin Boolean @default(false) accounts Account[] sessions Session[] + settings UserSettings? } model VerificationToken { @@ -67,3 +68,18 @@ model RegistrationToken { token String @unique expires DateTime } + +model UserSettings { + id String @id @default(cuid()) + userId String + colorScheme String @default("environment") // environment, light, dark + language String @default("en") + searchTemplate String @default("https://google.com/search?q=%s") + openSearchInNewTab Boolean @default(true) + disablePingPulse Boolean @default(false) + replacePingWithIcons Boolean @default(false) + useDebugLanguage Boolean @default(false) + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@unique([userId]) +} diff --git a/src/components/layout/header/SettingsMenu.tsx b/src/components/layout/header/SettingsMenu.tsx index 01cc9c34b..514391a16 100644 --- a/src/components/layout/header/SettingsMenu.tsx +++ b/src/components/layout/header/SettingsMenu.tsx @@ -35,7 +35,6 @@ export function SettingsMenu({ newVersionAvailable }: { newVersionAvailable: str - {!editModeEnabled && ( } onClick={drawer.open}> {t('sections.settings')} diff --git a/src/hooks/use-colorscheme.ts b/src/hooks/use-colorscheme.ts new file mode 100644 index 000000000..46aafa659 --- /dev/null +++ b/src/hooks/use-colorscheme.ts @@ -0,0 +1,27 @@ +import { ColorScheme } from '@mantine/core'; +import { useHotkeys } from '@mantine/hooks'; +import { setCookie } from 'cookies-next'; +import { Session } from 'next-auth'; +import { useState } from 'react'; +import { api } from '~/utils/api'; + +export const useColorScheme = (defaultValue: ColorScheme, session: Session) => { + const [colorScheme, setColorScheme] = useState(defaultValue); + const { mutateAsync } = api.user.changeColorScheme.useMutation(); + + const toggleColorScheme = async () => { + const newColorScheme = colorScheme === 'dark' ? 'light' : 'dark'; + setColorScheme(newColorScheme); + setCookie('color-scheme', newColorScheme); + if (session && new Date(session.expires) > new Date()) { + await mutateAsync({ colorScheme: newColorScheme }); + } + }; + + useHotkeys([['mod+J', () => void toggleColorScheme()]]); + + return { + colorScheme, + toggleColorScheme, + }; +}; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 9c40390da..a42263082 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -1,5 +1,4 @@ import { ColorScheme, ColorSchemeProvider, MantineProvider, MantineTheme } from '@mantine/core'; -import { useColorScheme, useHotkeys, useLocalStorage } from '@mantine/hooks'; import { ModalsProvider } from '@mantine/modals'; import { Notifications } from '@mantine/notifications'; import AsyncStorage from '@react-native-async-storage/async-storage'; @@ -7,7 +6,7 @@ import { createAsyncStoragePersister } from '@tanstack/query-async-storage-persi import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client'; import Consola from 'consola'; -import { getCookie } from 'cookies-next'; +import { getCookie, setCookie } from 'cookies-next'; import { GetServerSidePropsContext } from 'next'; import { Session } from 'next-auth'; import { SessionProvider, getSession } from 'next-auth/react'; @@ -17,8 +16,10 @@ import Head from 'next/head'; import { useEffect, useState } from 'react'; import 'video.js/dist/video-js.css'; import { env } from '~/env.js'; +import { useColorScheme } from '~/hooks/use-colorscheme'; import { ConfigType } from '~/types/config'; import { api } from '~/utils/api'; +import { colorSchemeParser } from '~/validations/user'; import nextI18nextConfig from '../../next-i18next.config.js'; import { ChangeAppPositionModal } from '../components/Dashboard/Modals/ChangePosition/ChangeAppPositionModal'; @@ -46,7 +47,6 @@ function App( colorScheme: ColorScheme; packageAttributes: ServerSidePackageAttributesType; editModeEnabled: boolean; - defaultColorScheme: ColorScheme; config?: ConfigType; configName?: string; session: Session; @@ -72,34 +72,20 @@ function App( setPrimaryShade, }; - // hook will return either 'dark' or 'light' on client - // and always 'light' during ssr as window.matchMedia is not available - const preferredColorScheme = useColorScheme(props.pageProps.defaultColorScheme); - const [colorScheme, setColorScheme] = useLocalStorage({ - key: 'mantine-color-scheme', - defaultValue: preferredColorScheme, - getInitialValueInEffect: true, - }); - const { setInitialPackageAttributes } = usePackageAttributesStore(); - const { setDisabled } = useEditModeInformationStore(); useEffect(() => { setInitialPackageAttributes(props.pageProps.packageAttributes); - - if (!props.pageProps.editModeEnabled) { - setDisabled(); - } }, []); - const toggleColorScheme = (value?: ColorScheme) => - setColorScheme(value || (colorScheme === 'dark' ? 'light' : 'dark')); - const asyncStoragePersister = createAsyncStoragePersister({ storage: AsyncStorage, }); - useHotkeys([['mod+J', () => toggleColorScheme()]]); + const { colorScheme, toggleColorScheme } = useColorScheme( + pageProps.colorScheme, + pageProps.session + ); return ( <> @@ -178,13 +164,25 @@ App.getInitialProps = async ({ ctx }: { ctx: GetServerSidePropsContext }) => { return { pageProps: { - colorScheme: getCookie('color-scheme', ctx) || 'light', + colorScheme: getActiveColorScheme(session, ctx), packageAttributes: getServiceSidePackageAttributes(), - editModeEnabled: process.env.DISABLE_EDIT_MODE !== 'true', - defaultColorScheme: env.DEFAULT_COLOR_SCHEME, session, }, }; }; export default appWithTranslation(api.withTRPC(App), nextI18nextConfig as any); + +const getActiveColorScheme = (session: Session | null, ctx: GetServerSidePropsContext) => { + const environmentColorScheme = env.DEFAULT_COLOR_SCHEME ?? 'light'; + const cookieValue = getCookie('color-scheme', ctx); + const activeColorScheme = colorSchemeParser.parse( + session?.user?.colorScheme ?? cookieValue ?? environmentColorScheme + ); + + if (cookieValue !== activeColorScheme) { + setCookie('color-scheme', activeColorScheme, ctx); + } + + return activeColorScheme === 'environment' ? environmentColorScheme : activeColorScheme; +}; diff --git a/src/server/api/routers/user.ts b/src/server/api/routers/user.ts index 50d859e95..638c4fb09 100644 --- a/src/server/api/routers/user.ts +++ b/src/server/api/routers/user.ts @@ -2,9 +2,9 @@ import { TRPCError } from '@trpc/server'; import bcrypt from 'bcrypt'; import { z } from 'zod'; import { hashPassword } from '~/utils/security'; -import { signUpFormSchema } from '~/validations/user'; +import { colorSchemeParser, signUpFormSchema } from '~/validations/user'; -import { createTRPCRouter, publicProcedure } from '../trpc'; +import { createTRPCRouter, protectedProcedure, publicProcedure } from '../trpc'; export const userRouter = createTRPCRouter({ register: publicProcedure @@ -50,6 +50,12 @@ export const userRouter = createTRPCRouter({ name: input.username, password: hashedPassword, salt: salt, + settings: { + create: { + colorScheme: colorSchemeParser.parse(ctx.cookies['color-scheme']), + language: ctx.cookies['config-locale'] ?? 'en', + }, + }, }, }); await ctx.prisma.registrationToken.delete({ @@ -63,4 +69,24 @@ export const userRouter = createTRPCRouter({ name: user.name, }; }), + changeColorScheme: protectedProcedure + .input( + z.object({ + colorScheme: colorSchemeParser, + }) + ) + .mutation(async ({ ctx, input }) => { + await ctx.prisma.user.update({ + where: { + id: ctx.session?.user?.id, + }, + data: { + settings: { + update: { + colorScheme: input.colorScheme, + }, + }, + }, + }); + }), }); diff --git a/src/server/api/trpc.ts b/src/server/api/trpc.ts index 3b83ba494..cef582ff5 100644 --- a/src/server/api/trpc.ts +++ b/src/server/api/trpc.ts @@ -25,6 +25,7 @@ import { prisma } from '../db'; interface CreateContextOptions { session: Session | null; + cookies: Partial>; } /** @@ -39,6 +40,7 @@ interface CreateContextOptions { */ const createInnerTRPCContext = (opts: CreateContextOptions) => ({ session: opts.session, + cookies: opts.cookies, prisma, }); @@ -56,6 +58,7 @@ export const createTRPCContext = async (opts: CreateNextContextOptions) => { return createInnerTRPCContext({ session, + cookies: req.cookies, }); }; diff --git a/src/server/auth.ts b/src/server/auth.ts index 70f593c1c..b60e83905 100644 --- a/src/server/auth.ts +++ b/src/server/auth.ts @@ -8,7 +8,7 @@ import Credentials from 'next-auth/providers/credentials'; import { prisma } from '~/server/db'; import EmptyNextAuthProvider from '~/utils/empty-provider'; import { fromDate, generateSessionToken } from '~/utils/session'; -import { signInSchema } from '~/validations/user'; +import { colorSchemeParser, signInSchema } from '~/validations/user'; /** * Module augmentation for `next-auth` types. Allows us to add custom properties to the `session` @@ -21,6 +21,8 @@ declare module 'next-auth' { user: DefaultSession['user'] & { id: string; isAdmin: boolean; + colorScheme: 'light' | 'dark' | 'environment'; + language: string; // ...other properties // role: UserRole; }; @@ -28,6 +30,8 @@ declare module 'next-auth' { interface User { isAdmin: boolean; + colorScheme: 'light' | 'dark' | 'environment'; + language: string; // ...other properties // role: UserRole; } @@ -64,9 +68,19 @@ export const constructAuthOptions = ( where: { id: user.id, }, + include: { + settings: { + select: { + colorScheme: true, + language: true, + }, + }, + }, }); session.user.isAdmin = userFromDatabase.isAdmin; + session.user.colorScheme = colorSchemeParser.parse(userFromDatabase.settings?.colorScheme); + session.user.language = userFromDatabase.settings?.language ?? 'en'; } return session; @@ -122,6 +136,14 @@ export const constructAuthOptions = ( where: { name: data.name, }, + include: { + settings: { + select: { + colorScheme: true, + language: true, + }, + }, + }, }); if (!user || !user.password) { @@ -142,6 +164,8 @@ export const constructAuthOptions = ( id: user.id, name: user.name, isAdmin: false, + colorScheme: colorSchemeParser.parse(user.settings?.colorScheme), + language: user.settings?.language ?? 'en', }; }, }), diff --git a/src/validations/user.ts b/src/validations/user.ts index 4ce6c0848..5f05755aa 100644 --- a/src/validations/user.ts +++ b/src/validations/user.ts @@ -18,3 +18,8 @@ export const signUpFormSchema = z } satisfies CustomErrorParams, path: ['passwordConfirmation'], }); + +export const colorSchemeParser = z + .enum(['light', 'dark', 'environment']) + .default('environment') + .catch('environment'); From c312828c798f11250710bd07e7e0251d544a848b Mon Sep 17 00:00:00 2001 From: Meier Lukas Date: Sat, 29 Jul 2023 11:35:34 +0200 Subject: [PATCH 006/233] =?UTF-8?q?=E2=9C=A8=20Add=20translation=20to=20us?= =?UTF-8?q?er=20settings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/constants.ts | 2 ++ .../Common/Language/LanguageSelect.tsx | 19 +++++++++++--- src/hooks/use-colorscheme.ts | 4 ++- src/pages/_app.tsx | 15 ++++++++--- src/server/api/routers/user.ts | 25 +++++++++++++++++-- src/tools/server/getServerSideTranslations.ts | 4 ++- 6 files changed, 57 insertions(+), 12 deletions(-) diff --git a/data/constants.ts b/data/constants.ts index b10f63d7e..6c43f365d 100644 --- a/data/constants.ts +++ b/data/constants.ts @@ -1,2 +1,4 @@ export const REPO_URL = 'ajnart/homarr'; export const ICON_PICKER_SLICE_LIMIT = 36; +export const COOKIE_LOCALE_KEY = 'config-locale'; +export const COOKIE_COLOR_SCHEME_KEY = 'color-scheme'; diff --git a/src/components/Settings/Common/Language/LanguageSelect.tsx b/src/components/Settings/Common/Language/LanguageSelect.tsx index 37dbdec9b..03f12e3d0 100644 --- a/src/components/Settings/Common/Language/LanguageSelect.tsx +++ b/src/components/Settings/Common/Language/LanguageSelect.tsx @@ -1,20 +1,25 @@ import { Group, Select, Stack, Text } from '@mantine/core'; import { showNotification } from '@mantine/notifications'; import { getCookie, setCookie } from 'cookies-next'; +import { useSession } from 'next-auth/react'; import { useTranslation } from 'next-i18next'; import { useRouter } from 'next/router'; import { forwardRef, useState } from 'react'; +import { api } from '~/utils/api'; +import { COOKIE_LOCALE_KEY } from '../../../../../data/constants'; import { Language, getLanguageByCode } from '../../../../tools/language'; export default function LanguageSelect() { + const { data: sessionData } = useSession(); const { t, i18n } = useTranslation('settings/general/internationalization'); const { changeLanguage } = i18n; - const configLocale = getCookie('config-locale'); + const configLocale = getCookie(COOKIE_LOCALE_KEY); const { locale, locales, pathname, query, asPath, push } = useRouter(); const [selectedLanguage, setSelectedLanguage] = useState( - (configLocale as string) ?? locale ?? 'en' + sessionData?.user.language ?? (configLocale as string) ?? locale ?? 'en' ); + const { mutateAsync } = api.user.changeLanguage.useMutation(); const data = locales ? locales.map((localeItem) => ({ @@ -30,12 +35,18 @@ export default function LanguageSelect() { const newLanguage = getLanguageByCode(value); changeLanguage(value) - .then(() => { - setCookie('config-locale', value, { + .then(async () => { + setCookie(COOKIE_LOCALE_KEY, value, { maxAge: 60 * 60 * 24 * 30, sameSite: 'strict', }); + if (sessionData?.user && new Date(sessionData.expires) > new Date()) { + await mutateAsync({ + language: value, + }); + } + push( { pathname, diff --git a/src/hooks/use-colorscheme.ts b/src/hooks/use-colorscheme.ts index 46aafa659..3f325111b 100644 --- a/src/hooks/use-colorscheme.ts +++ b/src/hooks/use-colorscheme.ts @@ -5,6 +5,8 @@ import { Session } from 'next-auth'; import { useState } from 'react'; import { api } from '~/utils/api'; +import { COOKIE_COLOR_SCHEME_KEY } from '../../data/constants'; + export const useColorScheme = (defaultValue: ColorScheme, session: Session) => { const [colorScheme, setColorScheme] = useState(defaultValue); const { mutateAsync } = api.user.changeColorScheme.useMutation(); @@ -12,7 +14,7 @@ export const useColorScheme = (defaultValue: ColorScheme, session: Session) => { const toggleColorScheme = async () => { const newColorScheme = colorScheme === 'dark' ? 'light' : 'dark'; setColorScheme(newColorScheme); - setCookie('color-scheme', newColorScheme); + setCookie(COOKIE_COLOR_SCHEME_KEY, newColorScheme); if (session && new Date(session.expires) > new Date()) { await mutateAsync({ colorScheme: newColorScheme }); } diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index a42263082..4c9a4c0d1 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -21,6 +21,7 @@ import { ConfigType } from '~/types/config'; import { api } from '~/utils/api'; import { colorSchemeParser } from '~/validations/user'; +import { COOKIE_COLOR_SCHEME_KEY, COOKIE_LOCALE_KEY } from '../../data/constants'; import nextI18nextConfig from '../../next-i18next.config.js'; import { ChangeAppPositionModal } from '../components/Dashboard/Modals/ChangePosition/ChangeAppPositionModal'; import { ChangeWidgetPositionModal } from '../components/Dashboard/Modals/ChangePosition/ChangeWidgetPositionModal'; @@ -162,6 +163,12 @@ App.getInitialProps = async ({ ctx }: { ctx: GetServerSidePropsContext }) => { const session = await getSession(ctx); + // Set the cookie language to the user language if it is not set correctly + const cookieLanguage = getCookie(COOKIE_LOCALE_KEY, ctx); + if (session?.user && session.user.language != cookieLanguage) { + setCookie(COOKIE_LOCALE_KEY, session.user.language, ctx); + } + return { pageProps: { colorScheme: getActiveColorScheme(session, ctx), @@ -175,13 +182,13 @@ export default appWithTranslation(api.withTRPC(App), nextI18nextConfig as a const getActiveColorScheme = (session: Session | null, ctx: GetServerSidePropsContext) => { const environmentColorScheme = env.DEFAULT_COLOR_SCHEME ?? 'light'; - const cookieValue = getCookie('color-scheme', ctx); + const cookieColorScheme = getCookie(COOKIE_COLOR_SCHEME_KEY, ctx); const activeColorScheme = colorSchemeParser.parse( - session?.user?.colorScheme ?? cookieValue ?? environmentColorScheme + session?.user?.colorScheme ?? cookieColorScheme ?? environmentColorScheme ); - if (cookieValue !== activeColorScheme) { - setCookie('color-scheme', activeColorScheme, ctx); + if (cookieColorScheme !== activeColorScheme) { + setCookie(COOKIE_COLOR_SCHEME_KEY, activeColorScheme, ctx); } return activeColorScheme === 'environment' ? environmentColorScheme : activeColorScheme; diff --git a/src/server/api/routers/user.ts b/src/server/api/routers/user.ts index 638c4fb09..6ba36d93a 100644 --- a/src/server/api/routers/user.ts +++ b/src/server/api/routers/user.ts @@ -4,6 +4,7 @@ import { z } from 'zod'; import { hashPassword } from '~/utils/security'; import { colorSchemeParser, signUpFormSchema } from '~/validations/user'; +import { COOKIE_COLOR_SCHEME_KEY, COOKIE_LOCALE_KEY } from '../../../../data/constants'; import { createTRPCRouter, protectedProcedure, publicProcedure } from '../trpc'; export const userRouter = createTRPCRouter({ @@ -52,8 +53,8 @@ export const userRouter = createTRPCRouter({ salt: salt, settings: { create: { - colorScheme: colorSchemeParser.parse(ctx.cookies['color-scheme']), - language: ctx.cookies['config-locale'] ?? 'en', + colorScheme: colorSchemeParser.parse(ctx.cookies[COOKIE_COLOR_SCHEME_KEY]), + language: ctx.cookies[COOKIE_LOCALE_KEY] ?? 'en', }, }, }, @@ -89,4 +90,24 @@ export const userRouter = createTRPCRouter({ }, }); }), + changeLanguage: protectedProcedure + .input( + z.object({ + language: z.string(), + }) + ) + .mutation(async ({ ctx, input }) => { + await ctx.prisma.user.update({ + where: { + id: ctx.session?.user?.id, + }, + data: { + settings: { + update: { + language: input.language, + }, + }, + }, + }); + }), }); diff --git a/src/tools/server/getServerSideTranslations.ts b/src/tools/server/getServerSideTranslations.ts index 5bf9388e8..3e803fc63 100644 --- a/src/tools/server/getServerSideTranslations.ts +++ b/src/tools/server/getServerSideTranslations.ts @@ -2,6 +2,8 @@ import { getCookie } from 'cookies-next'; import { IncomingMessage, ServerResponse } from 'http'; import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; +import { COOKIE_LOCALE_KEY } from '../../../data/constants'; + export const getServerSideTranslations = async ( namespaces: string[], requestLocale?: string, @@ -12,7 +14,7 @@ export const getServerSideTranslations = async ( return serverSideTranslations(requestLocale ?? 'en', namespaces); } - const configLocale = getCookie('config-locale', { req, res }); + const configLocale = getCookie(COOKIE_LOCALE_KEY, { req, res }); return serverSideTranslations((configLocale ?? requestLocale ?? 'en') as string, namespaces); }; From c165648d5b208cafc7bbde4d2969e96e17a5b3ac Mon Sep 17 00:00:00 2001 From: Meier Lukas Date: Sat, 29 Jul 2023 14:30:19 +0200 Subject: [PATCH 007/233] =?UTF-8?q?=F0=9F=8E=A8=20Improve=20color=20scheme?= =?UTF-8?q?=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../header/SettingsMenu/ColorSchemeSwitch.tsx | 5 +- src/env.js | 28 ++--- src/hooks/use-colorscheme.ts | 29 ----- src/hooks/use-colorscheme.tsx | 70 +++++++++++ src/pages/_app.tsx | 117 +++++++++--------- src/pages/api/trpc/[trpc].ts | 2 +- src/pages/migrate.tsx | 4 +- src/server/api/routers/user.ts | 23 ++++ src/server/db.ts | 4 +- src/tools/server/getPackageVersion.ts | 2 +- 10 files changed, 169 insertions(+), 115 deletions(-) delete mode 100644 src/hooks/use-colorscheme.ts create mode 100644 src/hooks/use-colorscheme.tsx diff --git a/src/components/layout/header/SettingsMenu/ColorSchemeSwitch.tsx b/src/components/layout/header/SettingsMenu/ColorSchemeSwitch.tsx index fb926496a..8adba3333 100644 --- a/src/components/layout/header/SettingsMenu/ColorSchemeSwitch.tsx +++ b/src/components/layout/header/SettingsMenu/ColorSchemeSwitch.tsx @@ -1,9 +1,10 @@ -import { Menu, useMantineColorScheme } from '@mantine/core'; +import { Menu } from '@mantine/core'; import { IconMoonStars, IconSun } from '@tabler/icons-react'; import { useTranslation } from 'next-i18next'; +import { useColorScheme } from '~/hooks/use-colorscheme'; export const ColorSchemeSwitch = () => { - const { colorScheme, toggleColorScheme } = useMantineColorScheme(); + const { colorScheme, toggleColorScheme } = useColorScheme(); const { t } = useTranslation('settings/general/theme-selector'); const Icon = colorScheme === 'dark' ? IconSun : IconMoonStars; diff --git a/src/env.js b/src/env.js index c45f748cb..8d208def5 100644 --- a/src/env.js +++ b/src/env.js @@ -1,8 +1,8 @@ const { z } = require('zod'); const { createEnv } = require('@t3-oss/env-nextjs'); -const portSchema = z.string().regex(/\d+/).transform(Number).optional() -const envSchema = z.enum(["development", "test", "production"]); +const portSchema = z.string().regex(/\d+/).transform(Number).optional(); +const envSchema = z.enum(['development', 'test', 'production']); const env = createEnv({ /** @@ -11,22 +11,17 @@ const env = createEnv({ */ server: { DATABASE_URL: z.string().url(), - NODE_ENV: envSchema, NEXTAUTH_SECRET: - process.env.NODE_ENV === "production" - ? z.string().min(1) - : z.string().min(1).optional(), + process.env.NODE_ENV === 'production' ? z.string().min(1) : z.string().min(1).optional(), NEXTAUTH_URL: z.preprocess( // This makes Vercel deployments not fail if you don't set NEXTAUTH_URL // Since NextAuth.js automatically uses the VERCEL_URL if present. (str) => process.env.VERCEL_URL ?? str, // VERCEL_URL doesn't include `https` so it cant be validated as a URL - process.env.VERCEL ? z.string().min(1) : z.string().url(), + process.env.VERCEL ? z.string().min(1) : z.string().url() ), - DEFAULT_COLOR_SCHEME: z.enum(['light', 'dark']).optional().default('light'), DOCKER_HOST: z.string().optional(), DOCKER_PORT: z.string().regex(/\d+/).transform(Number).optional(), - PORT: portSchema }, /** @@ -36,8 +31,9 @@ const env = createEnv({ */ client: { // NEXT_PUBLIC_CLIENTVAR: z.string().min(1), + NEXT_PUBLIC_DEFAULT_COLOR_SCHEME: z.enum(['light', 'dark']).optional().default('light'), NEXT_PUBLIC_PORT: portSchema, - NEXT_PUBLIC_NODE_ENV: envSchema + NEXT_PUBLIC_NODE_ENV: envSchema, }, /** @@ -46,21 +42,17 @@ const env = createEnv({ */ runtimeEnv: { DATABASE_URL: process.env.DATABASE_URL, - NODE_ENV: process.env.NODE_ENV, NEXTAUTH_SECRET: process.env.NEXTAUTH_SECRET, NEXTAUTH_URL: process.env.NEXTAUTH_URL, - NEXT_PUBLIC_DISABLE_EDIT_MODE: process.env.DISABLE_EDIT_MODE, - DISABLE_EDIT_MODE: process.env.DISABLE_EDIT_MODE, - DEFAULT_COLOR_SCHEME: process.env.DEFAULT_COLOR_SCHEME, DOCKER_HOST: process.env.DOCKER_HOST, DOCKER_PORT: process.env.DOCKER_PORT, VERCEL_URL: process.env.VERCEL_URL, - PORT: process.env.PORT, + NEXT_PUBLIC_DEFAULT_COLOR_SCHEME: process.env.DEFAULT_COLOR_SCHEME, NEXT_PUBLIC_PORT: process.env.PORT, - NEXT_PUBLIC_NODE_ENV: process.env.NODE_ENV + NEXT_PUBLIC_NODE_ENV: process.env.NODE_ENV, }, }); module.exports = { - env -} \ No newline at end of file + env, +}; diff --git a/src/hooks/use-colorscheme.ts b/src/hooks/use-colorscheme.ts deleted file mode 100644 index 3f325111b..000000000 --- a/src/hooks/use-colorscheme.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { ColorScheme } from '@mantine/core'; -import { useHotkeys } from '@mantine/hooks'; -import { setCookie } from 'cookies-next'; -import { Session } from 'next-auth'; -import { useState } from 'react'; -import { api } from '~/utils/api'; - -import { COOKIE_COLOR_SCHEME_KEY } from '../../data/constants'; - -export const useColorScheme = (defaultValue: ColorScheme, session: Session) => { - const [colorScheme, setColorScheme] = useState(defaultValue); - const { mutateAsync } = api.user.changeColorScheme.useMutation(); - - const toggleColorScheme = async () => { - const newColorScheme = colorScheme === 'dark' ? 'light' : 'dark'; - setColorScheme(newColorScheme); - setCookie(COOKIE_COLOR_SCHEME_KEY, newColorScheme); - if (session && new Date(session.expires) > new Date()) { - await mutateAsync({ colorScheme: newColorScheme }); - } - }; - - useHotkeys([['mod+J', () => void toggleColorScheme()]]); - - return { - colorScheme, - toggleColorScheme, - }; -}; diff --git a/src/hooks/use-colorscheme.tsx b/src/hooks/use-colorscheme.tsx new file mode 100644 index 000000000..c766a7bbb --- /dev/null +++ b/src/hooks/use-colorscheme.tsx @@ -0,0 +1,70 @@ +import { ColorScheme as MantineColorScheme } from '@mantine/core'; +import { useHotkeys } from '@mantine/hooks'; +import { setCookie } from 'cookies-next'; +import { Session } from 'next-auth'; +import { createContext, useContext, useState } from 'react'; +import { api } from '~/utils/api'; + +import { COOKIE_COLOR_SCHEME_KEY } from '../../data/constants'; + +export type ColorScheme = 'dark' | 'light' | 'environment'; + +export const ColorSchemeContext = createContext<{ + colorScheme: MantineColorScheme; + settings: ColorScheme; + toggleColorScheme: () => Promise; + setColorScheme: (colorScheme: ColorScheme) => void; +} | null>(null); + +type ColorSchemeProviderProps = { + activeColorScheme: ColorScheme; + environmentColorScheme: MantineColorScheme; + session: Session; + children: (colorScheme: MantineColorScheme) => React.ReactNode; +}; + +export const ColorSchemeProvider = ({ + activeColorScheme, + environmentColorScheme, + session, + children, +}: ColorSchemeProviderProps) => { + const [colorScheme, setColorScheme] = useState(activeColorScheme); + const { mutateAsync } = api.user.changeColorScheme.useMutation(); + + const toggleColorScheme = async () => { + const newColorScheme = colorScheme === 'dark' ? 'light' : 'dark'; + setColorScheme(newColorScheme); + setCookie(COOKIE_COLOR_SCHEME_KEY, newColorScheme); + if (session && new Date(session.expires) > new Date()) { + await mutateAsync({ colorScheme: newColorScheme }); + } + }; + + const changeColorScheme = (colorScheme: ColorScheme) => setColorScheme(colorScheme); + + useHotkeys([['mod+J', () => void toggleColorScheme()]]); + + const mantineColorScheme = colorScheme === 'environment' ? environmentColorScheme : colorScheme; + + return ( + + {children(mantineColorScheme)} + + ); +}; + +export const useColorScheme = () => { + const context = useContext(ColorSchemeContext); + if (!context) { + throw new Error('useColorScheme must be used within a ColorSchemeProvider'); + } + return context; +}; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 4c9a4c0d1..bac770a93 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -1,4 +1,4 @@ -import { ColorScheme, ColorSchemeProvider, MantineProvider, MantineTheme } from '@mantine/core'; +import { ColorScheme as MantineColorScheme, MantineProvider, MantineTheme } from '@mantine/core'; import { ModalsProvider } from '@mantine/modals'; import { Notifications } from '@mantine/notifications'; import AsyncStorage from '@react-native-async-storage/async-storage'; @@ -16,7 +16,7 @@ import Head from 'next/head'; import { useEffect, useState } from 'react'; import 'video.js/dist/video-js.css'; import { env } from '~/env.js'; -import { useColorScheme } from '~/hooks/use-colorscheme'; +import { ColorScheme, ColorSchemeProvider } from '~/hooks/use-colorscheme'; import { ConfigType } from '~/types/config'; import { api } from '~/utils/api'; import { colorSchemeParser } from '~/validations/user'; @@ -45,7 +45,8 @@ import { theme } from '../tools/server/theme/theme'; function App( this: any, props: AppProps<{ - colorScheme: ColorScheme; + activeColorScheme: MantineColorScheme; + environmentColorScheme: MantineColorScheme; packageAttributes: ServerSidePackageAttributesType; editModeEnabled: boolean; config?: ConfigType; @@ -83,11 +84,6 @@ function App( storage: AsyncStorage, }); - const { colorScheme, toggleColorScheme } = useColorScheme( - pageProps.colorScheme, - pageProps.session - ); - return ( <> @@ -98,50 +94,52 @@ function App( client={queryClient} persistOptions={{ persister: asyncStoragePersister }} > - - - + {(colorScheme) => ( + + - - - - - - - - + primaryColor, + primaryShade, + colorScheme, + }} + withGlobalStyles + withNormalizeCSS + > + + + + + + + + + )} @@ -151,16 +149,12 @@ function App( } App.getInitialProps = async ({ ctx }: { ctx: GetServerSidePropsContext }) => { - if (process.env.DISABLE_EDIT_MODE === 'true') { - Consola.warn( - 'EXPERIMENTAL: You have disabled the edit mode. Modifications are no longer possible and any requests on the API will be dropped. If you want to disable this, unset the DISABLE_EDIT_MODE environment variable. This behaviour may be removed in future versions of Homarr' + if (env.NEXT_PUBLIC_DEFAULT_COLOR_SCHEME !== 'light') { + Consola.debug( + `Overriding the default color scheme with ${env.NEXT_PUBLIC_DEFAULT_COLOR_SCHEME}` ); } - if (env.DEFAULT_COLOR_SCHEME !== 'light') { - Consola.debug(`Overriding the default color scheme with ${env.DEFAULT_COLOR_SCHEME}`); - } - const session = await getSession(ctx); // Set the cookie language to the user language if it is not set correctly @@ -171,7 +165,7 @@ App.getInitialProps = async ({ ctx }: { ctx: GetServerSidePropsContext }) => { return { pageProps: { - colorScheme: getActiveColorScheme(session, ctx), + ...getActiveColorScheme(session, ctx), packageAttributes: getServiceSidePackageAttributes(), session, }, @@ -181,7 +175,7 @@ App.getInitialProps = async ({ ctx }: { ctx: GetServerSidePropsContext }) => { export default appWithTranslation(api.withTRPC(App), nextI18nextConfig as any); const getActiveColorScheme = (session: Session | null, ctx: GetServerSidePropsContext) => { - const environmentColorScheme = env.DEFAULT_COLOR_SCHEME ?? 'light'; + const environmentColorScheme = env.NEXT_PUBLIC_DEFAULT_COLOR_SCHEME ?? 'light'; const cookieColorScheme = getCookie(COOKIE_COLOR_SCHEME_KEY, ctx); const activeColorScheme = colorSchemeParser.parse( session?.user?.colorScheme ?? cookieColorScheme ?? environmentColorScheme @@ -191,5 +185,8 @@ const getActiveColorScheme = (session: Session | null, ctx: GetServerSidePropsCo setCookie(COOKIE_COLOR_SCHEME_KEY, activeColorScheme, ctx); } - return activeColorScheme === 'environment' ? environmentColorScheme : activeColorScheme; + return { + activeColorScheme: activeColorScheme, + environmentColorScheme, + }; }; diff --git a/src/pages/api/trpc/[trpc].ts b/src/pages/api/trpc/[trpc].ts index 7bec97bf0..8b7cd11a4 100644 --- a/src/pages/api/trpc/[trpc].ts +++ b/src/pages/api/trpc/[trpc].ts @@ -9,7 +9,7 @@ export default createNextApiHandler({ router: rootRouter, createContext: createTRPCContext, onError: - env.NODE_ENV === 'development' + env.NEXT_PUBLIC_NODE_ENV === 'development' ? ({ path, error }) => { Consola.error(`❌ tRPC failed on ${path ?? ''}: ${error.message}`); } diff --git a/src/pages/migrate.tsx b/src/pages/migrate.tsx index 9b13a79b8..5c8c7e3d8 100644 --- a/src/pages/migrate.tsx +++ b/src/pages/migrate.tsx @@ -20,7 +20,6 @@ import { ThemeIcon, Title, createStyles, - useMantineColorScheme, useMantineTheme, } from '@mantine/core'; import { @@ -37,6 +36,7 @@ import fs from 'fs'; import { GetServerSidePropsContext } from 'next'; import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; import React, { useEffect, useState } from 'react'; +import { useColorScheme } from '~/hooks/use-colorscheme'; import { Logo } from '../components/layout/Logo'; import { usePrimaryGradient } from '../components/layout/useGradient'; @@ -229,7 +229,7 @@ export default function ServerError({ configs }: { configs: any }) { } function SwitchToggle() { - const { colorScheme, toggleColorScheme } = useMantineColorScheme(); + const { colorScheme, toggleColorScheme } = useColorScheme(); const theme = useMantineTheme(); return ( diff --git a/src/server/api/routers/user.ts b/src/server/api/routers/user.ts index 6ba36d93a..d5b5e5d09 100644 --- a/src/server/api/routers/user.ts +++ b/src/server/api/routers/user.ts @@ -110,4 +110,27 @@ export const userRouter = createTRPCRouter({ }, }); }), + getWithSettings: protectedProcedure.query(async ({ ctx }) => { + const user = await ctx.prisma.user.findUnique({ + where: { + id: ctx.session?.user?.id, + }, + include: { + settings: true, + }, + }); + + if (!user || !user.settings) { + throw new TRPCError({ + code: 'NOT_FOUND', + message: 'User not found', + }); + } + + return { + id: user.id, + name: user.name, + settings: user.settings, + }; + }), }); diff --git a/src/server/db.ts b/src/server/db.ts index 963fba477..9fb47cb40 100644 --- a/src/server/db.ts +++ b/src/server/db.ts @@ -8,7 +8,7 @@ const globalForPrisma = globalThis as unknown as { export const prisma = globalForPrisma.prisma ?? new PrismaClient({ - log: env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'], + log: env.NEXT_PUBLIC_NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'], }); -if (env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma; +if (env.NEXT_PUBLIC_NODE_ENV !== 'production') globalForPrisma.prisma = prisma; diff --git a/src/tools/server/getPackageVersion.ts b/src/tools/server/getPackageVersion.ts index cb7986d54..3197a3ca3 100644 --- a/src/tools/server/getPackageVersion.ts +++ b/src/tools/server/getPackageVersion.ts @@ -4,7 +4,7 @@ import packageJson from '../../../package.json'; const getServerPackageVersion = (): string | undefined => packageJson.version; -const getServerNodeEnvironment = () => env.NODE_ENV; +const getServerNodeEnvironment = () => env.NEXT_PUBLIC_NODE_ENV; const getDependencies = (): PackageJsonDependencies => packageJson.dependencies; From 3b01a42b156426413bd3edbf8726353a708fdbe7 Mon Sep 17 00:00:00 2001 From: Manuel Date: Sat, 29 Jul 2023 14:49:44 +0200 Subject: [PATCH 008/233] =?UTF-8?q?=E2=9C=A8=20Add=20management=20layout?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 3 +- .../layout/admin/main-admin.layout.tsx | 164 ++++++++++++++++++ src/pages/user/settings.tsx | 12 ++ 3 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 src/components/layout/admin/main-admin.layout.tsx create mode 100644 src/pages/user/settings.tsx diff --git a/package.json b/package.json index 25becea57..6391112b4 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,8 @@ "test": "vitest", "test:ui": "vitest --ui", "test:run": "vitest run", - "test:coverage": "vitest run --coverage" + "test:coverage": "vitest run --coverage", + "postinstall": "prisma generate" }, "dependencies": { "@ctrl/deluge": "^4.1.0", diff --git a/src/components/layout/admin/main-admin.layout.tsx b/src/components/layout/admin/main-admin.layout.tsx new file mode 100644 index 000000000..f0090b38f --- /dev/null +++ b/src/components/layout/admin/main-admin.layout.tsx @@ -0,0 +1,164 @@ +import { + Alert, + AppShell, + Avatar, + Box, + Flex, + Footer, + Group, + Header, + Menu, + NavLink, + Navbar, + Paper, + Text, + TextInput, + ThemeIcon, + UnstyledButton, +} from '@mantine/core'; +import { + IconAdjustmentsAlt, + IconAlertTriangle, + IconBook2, + IconBrandDiscord, + IconBrandGithub, + IconDashboard, + IconGitFork, + IconHome, + IconLogout, + IconMailForward, + IconQuestionMark, + IconSun, + IconUser, + IconUserSearch, +} from '@tabler/icons-react'; +import { signOut } from 'next-auth/react'; +import { useTranslation } from 'next-i18next'; +import Image from 'next/image'; +import Link from 'next/link'; +import { ReactNode } from 'react'; +import { usePackageAttributesStore } from '~/tools/client/zustands/usePackageAttributesStore'; + +import { Logo } from '../Logo'; + +interface MainLayoutProps { + children: ReactNode; +} + +export const MainLayout = ({ children }: MainLayoutProps) => { + const { t } = useTranslation(); + const { attributes } = usePackageAttributesStore(); + return ( + + + + + + } + label="Home" + component={Link} + href="/admin/users" + /> + + + + } + > + } + label="Manage" + component={Link} + href="/admin/users" + /> + } + label="Invites" + component={Link} + href="/admin/users/invites" + /> + + + + + } + > + } label="Documentation" /> + } label="Report an issue / bug" /> + } label="Ask a question" /> + } label="Contribute" /> + + + + } + header={ +
+ + + + + + + + + + + + + + }>Switch theme + }>View Profile + }>Default Dashboard + + } + color="red" + onClick={() => signOut()} + > + Logout + + + + + + +
+ } + footer={ +
+ + + + + Homarr + + {attributes.packageVersion && ( + + {attributes.packageVersion} + + )} + + +
+ } + > + + {children} + +
+ ); +}; diff --git a/src/pages/user/settings.tsx b/src/pages/user/settings.tsx new file mode 100644 index 000000000..20e4c55fa --- /dev/null +++ b/src/pages/user/settings.tsx @@ -0,0 +1,12 @@ +import { Title } from "@mantine/core"; +import { MainLayout } from "~/components/layout/admin/main-admin.layout"; + +const SettingsPage = () => { + return ( + + Good morning, Manicraft1001 + + ) +} + +export default SettingsPage; \ No newline at end of file From 5cac368926a1154be5b5f0f1feba9fbfb5b90580 Mon Sep 17 00:00:00 2001 From: Manuel Date: Sat, 29 Jul 2023 14:59:11 +0200 Subject: [PATCH 009/233] =?UTF-8?q?=E2=9C=A8=20Add=20correct=20page=20rout?= =?UTF-8?q?ing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/layout/admin/main-admin.layout.tsx | 15 ++++++--------- src/pages/{user/settings.tsx => manage/index.tsx} | 4 ++-- src/pages/manage/preferences.tsx | 12 ++++++++++++ src/pages/manage/users/index.tsx | 12 ++++++++++++ src/pages/manage/users/invites.tsx | 12 ++++++++++++ 5 files changed, 44 insertions(+), 11 deletions(-) rename src/pages/{user/settings.tsx => manage/index.tsx} (79%) create mode 100644 src/pages/manage/preferences.tsx create mode 100644 src/pages/manage/users/index.tsx create mode 100644 src/pages/manage/users/invites.tsx diff --git a/src/components/layout/admin/main-admin.layout.tsx b/src/components/layout/admin/main-admin.layout.tsx index f0090b38f..94437415e 100644 --- a/src/components/layout/admin/main-admin.layout.tsx +++ b/src/components/layout/admin/main-admin.layout.tsx @@ -1,8 +1,6 @@ import { - Alert, AppShell, Avatar, - Box, Flex, Footer, Group, @@ -17,8 +15,6 @@ import { UnstyledButton, } from '@mantine/core'; import { - IconAdjustmentsAlt, - IconAlertTriangle, IconBook2, IconBrandDiscord, IconBrandGithub, @@ -31,6 +27,7 @@ import { IconSun, IconUser, IconUserSearch, + IconUsers, } from '@tabler/icons-react'; import { signOut } from 'next-auth/react'; import { useTranslation } from 'next-i18next'; @@ -66,7 +63,7 @@ export const MainLayout = ({ children }: MainLayoutProps) => { } label="Home" component={Link} - href="/admin/users" + href="/manage/" /> { } > } + icon={} label="Manage" component={Link} - href="/admin/users" + href="/manage/users" /> } label="Invites" component={Link} - href="/admin/users/invites" + href="/manage/users/invites" /> { header={
- + diff --git a/src/pages/user/settings.tsx b/src/pages/manage/index.tsx similarity index 79% rename from src/pages/user/settings.tsx rename to src/pages/manage/index.tsx index 20e4c55fa..1b739b552 100644 --- a/src/pages/user/settings.tsx +++ b/src/pages/manage/index.tsx @@ -1,7 +1,7 @@ import { Title } from "@mantine/core"; import { MainLayout } from "~/components/layout/admin/main-admin.layout"; -const SettingsPage = () => { +const ManagementPage = () => { return ( Good morning, Manicraft1001 @@ -9,4 +9,4 @@ const SettingsPage = () => { ) } -export default SettingsPage; \ No newline at end of file +export default ManagementPage; \ No newline at end of file diff --git a/src/pages/manage/preferences.tsx b/src/pages/manage/preferences.tsx new file mode 100644 index 000000000..6da0ccd91 --- /dev/null +++ b/src/pages/manage/preferences.tsx @@ -0,0 +1,12 @@ +import { Title } from "@mantine/core"; +import { MainLayout } from "~/components/layout/admin/main-admin.layout"; + +const PreferencesPage = () => { + return ( + + Preferences + + ) +} + +export default PreferencesPage \ No newline at end of file diff --git a/src/pages/manage/users/index.tsx b/src/pages/manage/users/index.tsx new file mode 100644 index 000000000..80c614327 --- /dev/null +++ b/src/pages/manage/users/index.tsx @@ -0,0 +1,12 @@ +import { Title } from "@mantine/core"; +import { MainLayout } from "~/components/layout/admin/main-admin.layout"; + +const ManageUsersPage = () => { + return ( + + Manage users + + ) +} + +export default ManageUsersPage; \ No newline at end of file diff --git a/src/pages/manage/users/invites.tsx b/src/pages/manage/users/invites.tsx new file mode 100644 index 000000000..ceda212c4 --- /dev/null +++ b/src/pages/manage/users/invites.tsx @@ -0,0 +1,12 @@ +import { Title } from "@mantine/core"; +import { MainLayout } from "~/components/layout/admin/main-admin.layout"; + +const ManageUserInvitesPage = () => { + return ( + + Manage user invites + + ) +} + +export default ManageUserInvitesPage; \ No newline at end of file From 79d13274b5b420a34030c6fabfb5852c67a342b2 Mon Sep 17 00:00:00 2001 From: Meier Lukas Date: Sat, 29 Jul 2023 15:21:00 +0200 Subject: [PATCH 010/233] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Move=20authenticat?= =?UTF-8?q?ion=20pages=20to=20/auth?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../invite/[inviteId].tsx} | 19 ++++++++++++------- src/pages/{ => auth}/login.tsx | 2 +- src/server/auth.ts | 4 ++-- 3 files changed, 15 insertions(+), 10 deletions(-) rename src/pages/{register.tsx => auth/invite/[inviteId].tsx} (88%) rename src/pages/{ => auth}/login.tsx (97%) diff --git a/src/pages/register.tsx b/src/pages/auth/invite/[inviteId].tsx similarity index 88% rename from src/pages/register.tsx rename to src/pages/auth/invite/[inviteId].tsx index 88152ba77..f3c96753d 100644 --- a/src/pages/register.tsx +++ b/src/pages/auth/invite/[inviteId].tsx @@ -8,13 +8,12 @@ import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; import { useRouter } from 'next/router'; import { z } from 'zod'; import { prisma } from '~/server/db'; +import { registerNamespaces } from '~/tools/server/translation-namespaces'; import { api } from '~/utils/api'; import { useI18nZodResolver } from '~/utils/i18n-zod-resolver'; import { signUpFormSchema } from '~/validations/user'; -import { registerNamespaces } from '../tools/server/translation-namespaces'; - -export default function LoginPage() { +export default function AuthInvitePage() { const { t } = useTranslation('authentication/register'); const { i18nZodResolver } = useI18nZodResolver(); const router = useRouter(); @@ -111,10 +110,15 @@ export default function LoginPage() { const queryParamsSchema = z.object({ token: z.string(), }); +const routeParamsSchema = z.object({ + inviteId: z.string(), +}); -export const getServerSideProps: GetServerSideProps = async ({ locale, query }) => { - const result = queryParamsSchema.safeParse(query); - if (!result.success) { +export const getServerSideProps: GetServerSideProps = async ({ locale, query, params }) => { + const queryParams = queryParamsSchema.safeParse(query); + const routeParams = routeParamsSchema.safeParse(params); + console.log(queryParams, routeParams); + if (!queryParams.success || !routeParams.success) { return { notFound: true, }; @@ -122,7 +126,8 @@ export const getServerSideProps: GetServerSideProps = async ({ locale, query }) const token = await prisma.registrationToken.findUnique({ where: { - token: result.data.token, + id: routeParams.data.inviteId, + token: queryParams.data.token, }, }); diff --git a/src/pages/login.tsx b/src/pages/auth/login.tsx similarity index 97% rename from src/pages/login.tsx rename to src/pages/auth/login.tsx index c885c29c2..e32006f76 100644 --- a/src/pages/login.tsx +++ b/src/pages/auth/login.tsx @@ -21,7 +21,7 @@ import { useRouter } from 'next/router'; import { z } from 'zod'; import { signInSchema } from '~/validations/user'; -import { loginNamespaces } from '../tools/server/translation-namespaces'; +import { loginNamespaces } from '../../tools/server/translation-namespaces'; export default function LoginPage() { const { t } = useTranslation(['authentication/login']); diff --git a/src/server/auth.ts b/src/server/auth.ts index b60e83905..486dfc936 100644 --- a/src/server/auth.ts +++ b/src/server/auth.ts @@ -115,8 +115,8 @@ export const constructAuthOptions = ( maxAge: sessionMaxAgeInSeconds, }, pages: { - signIn: '/login', - error: '/login', + signIn: '/auth/login', + error: '/auth/login', }, adapter: PrismaAdapter(prisma), providers: [ From 76e3bc28e5c4e57893619ddca92f2b8c7c1b911f Mon Sep 17 00:00:00 2001 From: Manuel Date: Sat, 29 Jul 2023 15:32:17 +0200 Subject: [PATCH 011/233] =?UTF-8?q?=E2=9C=A8=20Improve=20management=20land?= =?UTF-8?q?ing=20page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../layout/admin/main-admin.layout.tsx | 43 +++++- src/pages/manage/index.tsx | 123 ++++++++++++++++-- 2 files changed, 151 insertions(+), 15 deletions(-) diff --git a/src/components/layout/admin/main-admin.layout.tsx b/src/components/layout/admin/main-admin.layout.tsx index 94437415e..6895b9a25 100644 --- a/src/components/layout/admin/main-admin.layout.tsx +++ b/src/components/layout/admin/main-admin.layout.tsx @@ -1,6 +1,7 @@ import { AppShell, Avatar, + Box, Flex, Footer, Group, @@ -15,6 +16,7 @@ import { UnstyledButton, } from '@mantine/core'; import { + IconAlertTriangle, IconBook2, IconBrandDiscord, IconBrandGithub, @@ -94,17 +96,46 @@ export const MainLayout = ({ children }: MainLayoutProps) => { } > - } label="Documentation" /> - } label="Report an issue / bug" /> - } label="Ask a question" /> - } label="Contribute" /> + } + component="a" + href="https://homarr.dev/docs/about" + label="Documentation" + /> + } + component="a" + href="https://github.com/ajnart/homarr/issues/new/choose" + label="Report an issue / bug" + /> + } + component="a" + href="https://discord.com/invite/aCsmEV5RgA" + label="Community Discord" + /> + } + component="a" + href="https://github.com/ajnart/homarr" + label="Contribute" + /> } header={ -
- +
+ + + + + This is an experimental feature of Homarr. Please report any issues to the official + Homarr team. + + + + diff --git a/src/pages/manage/index.tsx b/src/pages/manage/index.tsx index 1b739b552..cec1f4121 100644 --- a/src/pages/manage/index.tsx +++ b/src/pages/manage/index.tsx @@ -1,12 +1,117 @@ -import { Title } from "@mantine/core"; -import { MainLayout } from "~/components/layout/admin/main-admin.layout"; +import { + Box, + Card, + Group, + SimpleGrid, + Stack, + Text, + Title, + UnstyledButton, + createStyles, +} from '@mantine/core'; +import { IconArrowRight } from '@tabler/icons-react'; +import Image from 'next/image'; +import { MainLayout } from '~/components/layout/admin/main-admin.layout'; const ManagementPage = () => { - return ( - - Good morning, Manicraft1001 - - ) -} + const { classes } = useStyles(); + return ( + + + + + + Welcome back, Manicraft1001 + + + Are you ready to organize? +
+ You currently have 3 dashboards with 39 apps on them. +
+
+ + + + + +
+
-export default ManagementPage; \ No newline at end of file + + Quick actions + + + + + + + New dashboard + Create a new dashboard + + + + + + + + + + Your dasboards + Show a list of all your dashboards + + + + + + + + + + Invite a new user + Create and send an invitation for registration + + + + + + + + + + Your preferences + Adjust language, colors and more + + + + + + +
+ ); +}; + +export default ManagementPage; + +const useStyles = createStyles((theme) => ({ + box: { + borderRadius: theme.radius.md, + backgroundColor: theme.colorScheme === 'dark' ? theme.colors.red[4] : theme.colors.red[1], + }, + boxTitle: { + color: theme.colors.red[6], + }, + quickActionCard: { + height: "100%", + backgroundColor: theme.colors.gray[2], + '&:hover': { + backgroundColor: theme.colors.gray[3], + }, + }, +})); From e4e1f2e32e315e385368ba80460f62b249d9f2be Mon Sep 17 00:00:00 2001 From: Manuel Date: Sat, 29 Jul 2023 16:08:58 +0200 Subject: [PATCH 012/233] =?UTF-8?q?=E2=9C=A8=20Add=20tRPC=20user=20query?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../layout/admin/main-admin.layout.tsx | 4 +- src/pages/manage/users/index.tsx | 104 ++++++++++++++++-- src/pages/manage/users/invites.tsx | 22 ++-- src/server/api/routers/user.ts | 34 +++++- 4 files changed, 144 insertions(+), 20 deletions(-) diff --git a/src/components/layout/admin/main-admin.layout.tsx b/src/components/layout/admin/main-admin.layout.tsx index 6895b9a25..ded91e20d 100644 --- a/src/components/layout/admin/main-admin.layout.tsx +++ b/src/components/layout/admin/main-admin.layout.tsx @@ -14,6 +14,7 @@ import { TextInput, ThemeIcon, UnstyledButton, + useMantineTheme, } from '@mantine/core'; import { IconAlertTriangle, @@ -47,11 +48,12 @@ interface MainLayoutProps { export const MainLayout = ({ children }: MainLayoutProps) => { const { t } = useTranslation(); const { attributes } = usePackageAttributesStore(); + const theme = useMantineTheme(); return ( { - return ( - - Manage users - - ) -} + const { isLoading, data, fetchNextPage, fetchPreviousPage } = api.user.getAll.useInfiniteQuery( + { + limit: 10, + }, + { + getNextPageParam: (lastPage) => lastPage.nextCursor, + } + ); -export default ManageUsersPage; \ No newline at end of file + const [activePage, _] = useState(1); + + return ( + + + Users • Homarr + + Manage users + + + + + + + + + + {data && ( + <> + + + + + + + + {data.pages[0].users.map((user) => ( + + + + ))} + +
User
+ + + + {user.name} + + + + + + + +
+ + + )} +
+ ); +}; + +export default ManageUsersPage; diff --git a/src/pages/manage/users/invites.tsx b/src/pages/manage/users/invites.tsx index ceda212c4..8c0762a23 100644 --- a/src/pages/manage/users/invites.tsx +++ b/src/pages/manage/users/invites.tsx @@ -1,12 +1,16 @@ -import { Title } from "@mantine/core"; -import { MainLayout } from "~/components/layout/admin/main-admin.layout"; +import { Title } from '@mantine/core'; +import { Head } from 'next/document'; +import { MainLayout } from '~/components/layout/admin/main-admin.layout'; const ManageUserInvitesPage = () => { - return ( - - Manage user invites - - ) -} + return ( + + + User invites • Homarr + + Manage user invites + + ); +}; -export default ManageUserInvitesPage; \ No newline at end of file +export default ManageUserInvitesPage; diff --git a/src/server/api/routers/user.ts b/src/server/api/routers/user.ts index d5b5e5d09..a6d417bcb 100644 --- a/src/server/api/routers/user.ts +++ b/src/server/api/routers/user.ts @@ -110,7 +110,7 @@ export const userRouter = createTRPCRouter({ }, }); }), - getWithSettings: protectedProcedure.query(async ({ ctx }) => { + getWithSettings: protectedProcedure.query(async ({ ctx, input }) => { const user = await ctx.prisma.user.findUnique({ where: { id: ctx.session?.user?.id, @@ -133,4 +133,36 @@ export const userRouter = createTRPCRouter({ settings: user.settings, }; }), + + getAll: publicProcedure + .input( + z.object({ + limit: z.number().min(1).max(100).nullish(), + cursor: z.number().nullish(), + }) + ) + .query(async ({ ctx, input }) => { + const limit = input.limit ?? 50; + const cursor = input.cursor; + const users = await ctx.prisma.user.findMany({ + take: limit + 1, // get an extra item at the end which we'll use as next cursor + cursor: cursor ? { myCursor: cursor } : undefined, + }); + + let nextCursor: typeof cursor | undefined = undefined; + if (users.length > limit) { + const nextItem = users.pop(); + nextCursor = nextItem!.myCursor; + } + + return { + users: users.map((user) => ({ + id: user.id, + name: user.name, + email: user.email, + emailVerified: user.emailVerified + })), + nextCursor, + }; + }), }); From 1038cc7ccfc206c56e64da3b58c080a2c3c75589 Mon Sep 17 00:00:00 2001 From: Meier Lukas Date: Sat, 29 Jul 2023 16:16:01 +0200 Subject: [PATCH 013/233] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Show=20edit=20butt?= =?UTF-8?q?ons=20only=20when=20user=20is=20admin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Modals/AboutModal/AboutModal.tsx | 28 ------------------- src/components/layout/header/Header.tsx | 11 +++++--- src/components/layout/header/SettingsMenu.tsx | 4 +-- src/hooks/useEditModeInformation.ts | 11 -------- 4 files changed, 8 insertions(+), 46 deletions(-) delete mode 100644 src/hooks/useEditModeInformation.ts diff --git a/src/components/Dashboard/Modals/AboutModal/AboutModal.tsx b/src/components/Dashboard/Modals/AboutModal/AboutModal.tsx index c5c70e641..bc3ae16a0 100644 --- a/src/components/Dashboard/Modals/AboutModal/AboutModal.tsx +++ b/src/components/Dashboard/Modals/AboutModal/AboutModal.tsx @@ -12,7 +12,6 @@ import { Table, Text, Title, - Tooltip, createStyles, } from '@mantine/core'; import { @@ -35,7 +34,6 @@ import { ReactNode } from 'react'; import { useConfigContext } from '../../../../config/provider'; import { useConfigStore } from '../../../../config/store'; -import { useEditModeInformationStore } from '../../../../hooks/useEditModeInformation'; import { usePackageAttributesStore } from '../../../../tools/client/zustands/usePackageAttributesStore'; import { useColorTheme } from '../../../../tools/color'; import Credits from '../../../Settings/Common/Credits'; @@ -201,7 +199,6 @@ interface ExtendedInitOptions extends InitOptions { const useInformationTableItems = (newVersionAvailable?: string): InformationTableItem[] => { const { attributes } = usePackageAttributesStore(); - const { editModeEnabled } = useEditModeInformationStore(); const { primaryColor } = useColorTheme(); const { configVersion } = useConfigContext(); @@ -209,31 +206,6 @@ const useInformationTableItems = (newVersionAvailable?: string): InformationTabl let items: InformationTableItem[] = []; - if (editModeEnabled) { - items = [ - ...items, - { - icon: , - label: 'experimental_disableEditMode', - content: ( - - WARNING - - ), - }, - ]; - } - if (i18n !== null) { const usedI18nNamespaces = i18n.reportNamespaces.getUsedNamespaces(); const initOptions = i18n.options as ExtendedInitOptions; diff --git a/src/components/layout/header/Header.tsx b/src/components/layout/header/Header.tsx index 4398efc54..0e74aa771 100644 --- a/src/components/layout/header/Header.tsx +++ b/src/components/layout/header/Header.tsx @@ -3,7 +3,6 @@ import { useQuery } from '@tanstack/react-query'; import { useSession } from 'next-auth/react'; import { REPO_URL } from '../../../../data/constants'; -import { useEditModeInformationStore } from '../../../hooks/useEditModeInformation'; import DockerMenuButton from '../../../modules/Docker/DockerModule'; import { usePackageAttributesStore } from '../../../tools/client/zustands/usePackageAttributesStore'; import { Logo } from '../Logo'; @@ -18,7 +17,7 @@ export function Header(props: any) { const { classes } = useStyles(); const { classes: cardClasses, cx } = useCardStyles(false); const { attributes } = usePackageAttributesStore(); - const { editModeEnabled } = useEditModeInformationStore(); + const { data: sessionData } = useSession(); const { data } = useQuery({ queryKey: ['github/latest'], @@ -43,8 +42,12 @@ export function Header(props: any) { noWrap > - {!editModeEnabled && } - + {sessionData?.user?.isAdmin && ( + <> + + + + )} - {!editModeEnabled && ( + {sessionData?.user?.isAdmin && ( } onClick={drawer.open}> {t('sections.settings')} diff --git a/src/hooks/useEditModeInformation.ts b/src/hooks/useEditModeInformation.ts deleted file mode 100644 index c59edc86c..000000000 --- a/src/hooks/useEditModeInformation.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { create } from 'zustand'; - -interface EditModeInformationStore { - editModeEnabled: boolean; - setDisabled: () => void; -} - -export const useEditModeInformationStore = create((set) => ({ - editModeEnabled: false, - setDisabled: () => set(() => ({ editModeEnabled: true })), -})); From 4c2e81a29dd06ed43e0ab8ebd5c137176a9ee1e8 Mon Sep 17 00:00:00 2001 From: Manuel Date: Sat, 29 Jul 2023 16:17:34 +0200 Subject: [PATCH 014/233] =?UTF-8?q?=E2=9C=A8=20Add=20stepper=20for=20creat?= =?UTF-8?q?ing=20new=20user?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/manage/users/create.tsx | 52 +++++++++++++++++++++++++++++++ src/pages/manage/users/index.tsx | 9 +++++- 2 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 src/pages/manage/users/create.tsx diff --git a/src/pages/manage/users/create.tsx b/src/pages/manage/users/create.tsx new file mode 100644 index 000000000..fda4a858a --- /dev/null +++ b/src/pages/manage/users/create.tsx @@ -0,0 +1,52 @@ +import { Stepper } from '@mantine/core'; +import { IconMailCheck, IconUser } from '@tabler/icons-react'; +import Head from 'next/head'; +import { useState } from 'react'; +import { MainLayout } from '~/components/layout/admin/main-admin.layout'; + +const CreateNewUserPage = () => { + const [active, setActive] = useState(1); + const nextStep = () => setActive((current) => (current < 3 ? current + 1 : current)); + const prevStep = () => setActive((current) => (current > 0 ? current - 1 : current)); + + return ( + + + Create user • Homarr + + + + } + label="First step" + description="Create an account" + > + Step 1 content: Create an account + + } + label="Second step" + description="Verify email" + > + Step 2 content: Verify email + + } + label="Final step" + description="Get full access" + > + Step 3 content: Get full access + + Completed, click back button to get to previous step + + + ); +}; + +export default CreateNewUserPage; diff --git a/src/pages/manage/users/index.tsx b/src/pages/manage/users/index.tsx index 4ee488c76..38cb1976a 100644 --- a/src/pages/manage/users/index.tsx +++ b/src/pages/manage/users/index.tsx @@ -13,6 +13,7 @@ import { } from '@mantine/core'; import { IconPlus, IconTrash } from '@tabler/icons-react'; import Head from 'next/head'; +import Link from 'next/link'; import { useState } from 'react'; import { MainLayout } from '~/components/layout/admin/main-admin.layout'; import { api } from '~/utils/api'; @@ -34,6 +35,7 @@ const ManageUsersPage = () => { Users • Homarr + Manage users @@ -49,7 +51,12 @@ const ManageUsersPage = () => { data={['React', 'Angular', 'Svelte', 'Vue']} variant="filled" /> - From e045081346c566bddeea4962266a490a2d263c0b Mon Sep 17 00:00:00 2001 From: Manuel Date: Sat, 29 Jul 2023 16:19:57 +0200 Subject: [PATCH 015/233] =?UTF-8?q?=F0=9F=92=84=20Fix=20darkmode=20styling?= =?UTF-8?q?=20for=20manage=20home=20page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/manage/index.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/pages/manage/index.tsx b/src/pages/manage/index.tsx index cec1f4121..7e1b52238 100644 --- a/src/pages/manage/index.tsx +++ b/src/pages/manage/index.tsx @@ -102,16 +102,17 @@ export default ManagementPage; const useStyles = createStyles((theme) => ({ box: { borderRadius: theme.radius.md, - backgroundColor: theme.colorScheme === 'dark' ? theme.colors.red[4] : theme.colors.red[1], + backgroundColor: + theme.colorScheme === 'dark' ? theme.fn.rgba(theme.colors.red[8], 0.1) : theme.colors.red[1], }, boxTitle: { color: theme.colors.red[6], }, quickActionCard: { - height: "100%", - backgroundColor: theme.colors.gray[2], + height: '100%', + backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[6] : theme.colors.gray[2], '&:hover': { - backgroundColor: theme.colors.gray[3], + backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[5] : theme.colors.gray[3], }, }, })); From e273c830b4fc28f0dcf856961939935920a5d00a Mon Sep 17 00:00:00 2001 From: Manuel Date: Sat, 29 Jul 2023 17:29:57 +0200 Subject: [PATCH 016/233] =?UTF-8?q?=E2=9C=A8=20Add=20create=20user=20form?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CreateNewUser/create-account-step.tsx | 62 ++++++++++ .../Admin/CreateNewUser/security-step.tsx | 75 ++++++++++++ src/pages/manage/users/create.tsx | 113 +++++++++++++++--- src/pages/manage/users/index.tsx | 8 +- src/server/api/routers/user.ts | 28 ++++- src/utils/security.ts | 2 +- 6 files changed, 263 insertions(+), 25 deletions(-) create mode 100644 src/components/Admin/CreateNewUser/create-account-step.tsx create mode 100644 src/components/Admin/CreateNewUser/security-step.tsx diff --git a/src/components/Admin/CreateNewUser/create-account-step.tsx b/src/components/Admin/CreateNewUser/create-account-step.tsx new file mode 100644 index 000000000..b06531559 --- /dev/null +++ b/src/components/Admin/CreateNewUser/create-account-step.tsx @@ -0,0 +1,62 @@ +import { Button, Card, Flex, TextInput } from '@mantine/core'; +import { useForm, zodResolver } from '@mantine/form'; +import { IconArrowRight, IconAt, IconUser } from '@tabler/icons-react'; +import { z } from 'zod'; + +interface CreateAccountStepProps { + nextStep: ({ eMail, username }: { username: string; eMail: string }) => void; +} + +export const CreateAccountStep = ({ nextStep }: CreateAccountStepProps) => { + const form = useForm({ + initialValues: { + username: '', + eMail: '', + }, + validateInputOnBlur: true, + validateInputOnChange: true, + validate: zodResolver(createAccountStepValidationSchema), + }); + + return ( + + } + label="Username" + variant="filled" + mb="md" + withAsterisk + {...form.getInputProps('username')} + /> + } + label="E-Mail" + variant="filled" + mb="md" + {...form.getInputProps('eMail')} + /> + + + + + + ); +}; + +export const createAccountStepValidationSchema = z.object({ + username: z.string().min(1).max(100), + eMail: z.string().email().or(z.literal('')), +}); diff --git a/src/components/Admin/CreateNewUser/security-step.tsx b/src/components/Admin/CreateNewUser/security-step.tsx new file mode 100644 index 000000000..0ba0a9d50 --- /dev/null +++ b/src/components/Admin/CreateNewUser/security-step.tsx @@ -0,0 +1,75 @@ +import { Button, Card, Flex, Group, PasswordInput } from '@mantine/core'; +import { useForm, zodResolver } from '@mantine/form'; +import { IconArrowLeft, IconArrowRight, IconPassword } from '@tabler/icons-react'; +import { z } from 'zod'; + +interface CreateAccountSecurityStepProps { + nextStep: ({ password }: { password: string }) => void; + prevStep: () => void; +} + +export const CreateAccountSecurityStep = ({ + nextStep, + prevStep, +}: CreateAccountSecurityStepProps) => { + const form = useForm({ + initialValues: { + password: '', + }, + validateInputOnBlur: true, + validateInputOnChange: true, + validate: zodResolver(createAccountSecurityStepValidationSchema), + }); + + return ( + + + } + label="Password" + variant="filled" + mb="md" + withAsterisk + style={{ + flexGrow: 1, + }} + {...form.getInputProps('password')} + /> + + + + + + + + + ); +}; + +const randomString = () => { + return window.crypto.getRandomValues(new BigUint64Array(1))[0].toString(36); +}; + +export const createAccountSecurityStepValidationSchema = z.object({ + password: z.string().min(10).max(50), +}); diff --git a/src/pages/manage/users/create.tsx b/src/pages/manage/users/create.tsx index fda4a858a..115d4cf24 100644 --- a/src/pages/manage/users/create.tsx +++ b/src/pages/manage/users/create.tsx @@ -1,47 +1,126 @@ -import { Stepper } from '@mantine/core'; -import { IconMailCheck, IconUser } from '@tabler/icons-react'; +import { Button, Card, Flex, Group, Stepper, Text, TextInput } from '@mantine/core'; +import { useForm, zodResolver } from '@mantine/form'; +import { + IconArrowRight, + IconAt, + IconCheck, + IconLock, + IconMailCheck, + IconPremiumRights, + IconSignRight, + IconUser, +} from '@tabler/icons-react'; import Head from 'next/head'; +import Link from 'next/link'; import { useState } from 'react'; +import { z } from 'zod'; +import { + CreateAccountStep, + createAccountStepValidationSchema, +} from '~/components/Admin/CreateNewUser/create-account-step'; +import { + CreateAccountSecurityStep, + createAccountSecurityStepValidationSchema, +} from '~/components/Admin/CreateNewUser/security-step'; import { MainLayout } from '~/components/layout/admin/main-admin.layout'; +import { api } from '~/utils/api'; const CreateNewUserPage = () => { - const [active, setActive] = useState(1); + const [active, setActive] = useState(0); const nextStep = () => setActive((current) => (current < 3 ? current + 1 : current)); const prevStep = () => setActive((current) => (current > 0 ? current - 1 : current)); + const form = useForm({ + initialValues: { + account: { + username: '', + eMail: '', + }, + security: { + password: '', + }, + }, + validate: zodResolver( + z.object({ + account: createAccountStepValidationSchema, + security: createAccountSecurityStepValidationSchema, + }) + ), + }); + + const { mutateAsync, isSuccess } = api.user.createUser.useMutation(); + return ( Create user • Homarr - + } label="First step" - description="Create an account" + description="Create account" > - Step 1 content: Create an account + { + form.setFieldValue('account', value); + nextStep(); + }} + /> + + } + label="Second step" + description="Password" + > + { + form.setFieldValue('security', value); + nextStep(); + }} + prevStep={prevStep} + /> } - label="Second step" - description="Verify email" - > - Step 2 content: Verify email - - } label="Final step" - description="Get full access" + description="Save to database" > - Step 3 content: Get full access + + + User creation has been prepared. Do you want to create the user and store it in the + database? + + + + + Completed, click back button to get to previous step diff --git a/src/pages/manage/users/index.tsx b/src/pages/manage/users/index.tsx index 38cb1976a..d60f421d2 100644 --- a/src/pages/manage/users/index.tsx +++ b/src/pages/manage/users/index.tsx @@ -28,7 +28,9 @@ const ManageUsersPage = () => { } ); - const [activePage, _] = useState(1); + const [activePage, _] = useState(0); + + console.log(data?.pages); return ( @@ -71,7 +73,7 @@ const ManageUsersPage = () => { - {data.pages[0].users.map((user) => ( + {data.pages[activePage].users.map((user) => ( @@ -92,7 +94,7 @@ const ManageUsersPage = () => { diff --git a/src/server/api/routers/user.ts b/src/server/api/routers/user.ts index a6d417bcb..02fa3b340 100644 --- a/src/server/api/routers/user.ts +++ b/src/server/api/routers/user.ts @@ -138,7 +138,7 @@ export const userRouter = createTRPCRouter({ .input( z.object({ limit: z.number().min(1).max(100).nullish(), - cursor: z.number().nullish(), + cursor: z.string().nullish(), }) ) .query(async ({ ctx, input }) => { @@ -146,13 +146,13 @@ export const userRouter = createTRPCRouter({ const cursor = input.cursor; const users = await ctx.prisma.user.findMany({ take: limit + 1, // get an extra item at the end which we'll use as next cursor - cursor: cursor ? { myCursor: cursor } : undefined, + cursor: cursor ? { id: cursor } : undefined, }); let nextCursor: typeof cursor | undefined = undefined; if (users.length > limit) { const nextItem = users.pop(); - nextCursor = nextItem!.myCursor; + nextCursor = nextItem!.id; } return { @@ -160,9 +160,29 @@ export const userRouter = createTRPCRouter({ id: user.id, name: user.name, email: user.email, - emailVerified: user.emailVerified + emailVerified: user.emailVerified, })), nextCursor, }; }), + createUser: publicProcedure + .input( + z.object({ + username: z.string(), + email: z.string().email().optional(), + password: z.string().min(8).max(100), + }) + ) + .mutation(async ({ ctx, input }) => { + const salt = bcrypt.genSaltSync(10); + const hashedPassword = hashPassword(input.password, salt); + await ctx.prisma.user.create({ + data: { + name: input.username, + email: input.email, + password: hashedPassword, + salt: salt, + }, + }); + }), }); diff --git a/src/utils/security.ts b/src/utils/security.ts index 395e9d29c..520a663c0 100644 --- a/src/utils/security.ts +++ b/src/utils/security.ts @@ -1,4 +1,4 @@ -import bcrypt from "bcrypt"; +import bcrypt from 'bcrypt'; export const hashPassword = (password: string, salt: string) => { return bcrypt.hashSync(password, salt); From ed23e388f958e045439e5c18259616966b1819f2 Mon Sep 17 00:00:00 2001 From: Manuel Date: Sat, 29 Jul 2023 20:12:49 +0200 Subject: [PATCH 017/233] =?UTF-8?q?=F0=9F=9A=B8=20Improve=20UX=20on=20crea?= =?UTF-8?q?te=20user=20page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modals/delete-user/delete-user.modal.tsx | 45 ++++++++++++++++++++ src/modals/modals.ts | 26 +++++++++++ src/pages/_app.tsx | 23 ++-------- src/pages/manage/users/create.tsx | 7 ++- src/pages/manage/users/index.tsx | 20 +++++++-- src/server/api/routers/user.ts | 14 ++++++ 6 files changed, 111 insertions(+), 24 deletions(-) create mode 100644 src/modals/delete-user/delete-user.modal.tsx create mode 100644 src/modals/modals.ts diff --git a/src/modals/delete-user/delete-user.modal.tsx b/src/modals/delete-user/delete-user.modal.tsx new file mode 100644 index 000000000..92d473c49 --- /dev/null +++ b/src/modals/delete-user/delete-user.modal.tsx @@ -0,0 +1,45 @@ +import { Button, Group, Stack, Text } from '@mantine/core'; +import { ContextModalProps, modals } from '@mantine/modals'; +import { api } from '~/utils/api'; + +export const DeleteUserModal = ({ + context, + id, + innerProps, +}: ContextModalProps<{ userId: string; username: string }>) => { + const apiContext = api.useContext(); + const { isLoading, mutateAsync } = api.user.deleteUser.useMutation({ + onSuccess: async () => { + await apiContext.user.getAll.invalidate(); + modals.close(id); + }, + }); + return ( + + + Are you sure, that you want to delete the user {innerProps.username}? This will delete data + associated with this account, but not any created dashboards by this user. + + + + + + + + ); +}; diff --git a/src/modals/modals.ts b/src/modals/modals.ts new file mode 100644 index 000000000..c5ace5220 --- /dev/null +++ b/src/modals/modals.ts @@ -0,0 +1,26 @@ +import { ChangeAppPositionModal } from '~/components/Dashboard/Modals/ChangePosition/ChangeAppPositionModal'; +import { ChangeWidgetPositionModal } from '~/components/Dashboard/Modals/ChangePosition/ChangeWidgetPositionModal'; +import { EditAppModal } from '~/components/Dashboard/Modals/EditAppModal/EditAppModal'; +import { SelectElementModal } from '~/components/Dashboard/Modals/SelectElement/SelectElementModal'; +import { WidgetsEditModal } from '~/components/Dashboard/Tiles/Widgets/WidgetsEditModal'; +import { WidgetsRemoveModal } from '~/components/Dashboard/Tiles/Widgets/WidgetsRemoveModal'; +import { CategoryEditModal } from '~/components/Dashboard/Wrappers/Category/CategoryEditModal'; + +import { DeleteUserModal } from './delete-user/delete-user.modal'; + +export const modals = { + editApp: EditAppModal, + selectElement: SelectElementModal, + integrationOptions: WidgetsEditModal, + integrationRemove: WidgetsRemoveModal, + categoryEditModal: CategoryEditModal, + changeAppPositionModal: ChangeAppPositionModal, + changeIntegrationPositionModal: ChangeWidgetPositionModal, + deleteUserModal: DeleteUserModal, +}; + +declare module '@mantine/modals' { + export interface MantineModalsOverride { + modals: typeof modals; + } +} diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index bac770a93..57003c9a3 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -16,22 +16,15 @@ import Head from 'next/head'; import { useEffect, useState } from 'react'; import 'video.js/dist/video-js.css'; import { env } from '~/env.js'; -import { ColorScheme, ColorSchemeProvider } from '~/hooks/use-colorscheme'; +import { ColorSchemeProvider } from '~/hooks/use-colorscheme'; +import { modals } from '~/modals/modals'; import { ConfigType } from '~/types/config'; import { api } from '~/utils/api'; import { colorSchemeParser } from '~/validations/user'; import { COOKIE_COLOR_SCHEME_KEY, COOKIE_LOCALE_KEY } from '../../data/constants'; import nextI18nextConfig from '../../next-i18next.config.js'; -import { ChangeAppPositionModal } from '../components/Dashboard/Modals/ChangePosition/ChangeAppPositionModal'; -import { ChangeWidgetPositionModal } from '../components/Dashboard/Modals/ChangePosition/ChangeWidgetPositionModal'; -import { EditAppModal } from '../components/Dashboard/Modals/EditAppModal/EditAppModal'; -import { SelectElementModal } from '../components/Dashboard/Modals/SelectElement/SelectElementModal'; -import { WidgetsEditModal } from '../components/Dashboard/Tiles/Widgets/WidgetsEditModal'; -import { WidgetsRemoveModal } from '../components/Dashboard/Tiles/Widgets/WidgetsRemoveModal'; -import { CategoryEditModal } from '../components/Dashboard/Wrappers/Category/CategoryEditModal'; import { ConfigProvider } from '../config/provider'; -import { useEditModeInformationStore } from '../hooks/useEditModeInformation'; import '../styles/global.scss'; import { usePackageAttributesStore } from '../tools/client/zustands/usePackageAttributesStore'; import { ColorTheme } from '../tools/color'; @@ -123,17 +116,7 @@ function App( > - + diff --git a/src/pages/manage/users/create.tsx b/src/pages/manage/users/create.tsx index 115d4cf24..bf09576d2 100644 --- a/src/pages/manage/users/create.tsx +++ b/src/pages/manage/users/create.tsx @@ -48,7 +48,12 @@ const CreateNewUserPage = () => { ), }); - const { mutateAsync, isSuccess } = api.user.createUser.useMutation(); + const context = api.useContext(); + const { mutateAsync, isSuccess } = api.user.createUser.useMutation({ + onSettled: () => { + void context.user.getAll.invalidate(); + } + }); return ( diff --git a/src/pages/manage/users/index.tsx b/src/pages/manage/users/index.tsx index d60f421d2..f4f4c0207 100644 --- a/src/pages/manage/users/index.tsx +++ b/src/pages/manage/users/index.tsx @@ -11,6 +11,7 @@ import { Text, Title, } from '@mantine/core'; +import { openContextModal } from '@mantine/modals'; import { IconPlus, IconTrash } from '@tabler/icons-react'; import Head from 'next/head'; import Link from 'next/link'; @@ -28,9 +29,9 @@ const ManageUsersPage = () => { } ); - const [activePage, _] = useState(0); + const { mutateAsync: deleteUserMutateAsync } = api.user.deleteUser.useMutation(); - console.log(data?.pages); + const [activePage, _] = useState(0); return ( @@ -82,7 +83,20 @@ const ManageUsersPage = () => { {user.name} - + { + openContextModal({ + modal: 'deleteUserModal', + title: Delete user ${user.name}, + innerProps: { + userId: user.id, + username: user.name ?? '' + } + }); + }} + color="red" + variant="light" + > diff --git a/src/server/api/routers/user.ts b/src/server/api/routers/user.ts index 02fa3b340..402e2e505 100644 --- a/src/server/api/routers/user.ts +++ b/src/server/api/routers/user.ts @@ -185,4 +185,18 @@ export const userRouter = createTRPCRouter({ }, }); }), + + deleteUser: publicProcedure + .input( + z.object({ + userId: z.string(), + }) + ) + .mutation(async ({ ctx, input }) => { + await ctx.prisma.user.delete({ + where: { + id: input.userId, + }, + }); + }), }); From 0c3d9f335cc314ac2e530c2ed7d02bd062b674c8 Mon Sep 17 00:00:00 2001 From: Meier Lukas Date: Sat, 29 Jul 2023 20:56:08 +0200 Subject: [PATCH 018/233] =?UTF-8?q?=E2=9C=A8=20Implement=20search=20for=20?= =?UTF-8?q?new=20header?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/layout/main.tsx | 23 +++ src/components/layout/new-header/Header.tsx | 83 +++++++++ src/components/layout/new-header/search.tsx | 180 ++++++++++++++++++++ src/pages/index.tsx | 5 +- src/server/api/routers/overseerr.ts | 7 +- 5 files changed, 294 insertions(+), 4 deletions(-) create mode 100644 src/components/layout/main.tsx create mode 100644 src/components/layout/new-header/Header.tsx create mode 100644 src/components/layout/new-header/search.tsx diff --git a/src/components/layout/main.tsx b/src/components/layout/main.tsx new file mode 100644 index 000000000..231d3d274 --- /dev/null +++ b/src/components/layout/main.tsx @@ -0,0 +1,23 @@ +import { AppShell, useMantineTheme } from '@mantine/core'; + +import { MainHeader } from './new-header/Header'; + +type MainLayoutProps = { + children: React.ReactNode; +}; + +export const MainLayout = ({ children }: MainLayoutProps) => { + const theme = useMantineTheme(); + return ( + } + > + {children} + + ); +}; diff --git a/src/components/layout/new-header/Header.tsx b/src/components/layout/new-header/Header.tsx new file mode 100644 index 000000000..5ac92e1fc --- /dev/null +++ b/src/components/layout/new-header/Header.tsx @@ -0,0 +1,83 @@ +import { + Anchor, + Avatar, + Box, + Flex, + Group, + Header, + Menu, + Text, + TextInput, + UnstyledButton, +} from '@mantine/core'; +import { + IconAlertTriangle, + IconDashboard, + IconLogout, + IconSun, + IconUserSearch, +} from '@tabler/icons-react'; +import { signOut } from 'next-auth/react'; +import Link from 'next/link'; + +import { Logo } from '../Logo'; +import { Search } from './search'; + +type MainHeaderProps = { + logoHref?: string; + showExperimental?: boolean; +}; + +export const MainHeader = ({ showExperimental = false, logoHref = '/' }: MainHeaderProps) => { + const headerHeight = showExperimental ? 60 + 30 : 60; + return ( +
+ + + + + + + + + + + + + + + + }>Switch theme + }>View Profile + }>Default Dashboard + + } color="red" onClick={() => signOut()}> + Logout + + + + + + +
+ ); +}; + +type ExperimentalHeaderNoteProps = { + visible?: boolean; +}; +const ExperimentalHeaderNote = ({ visible = false }: ExperimentalHeaderNoteProps) => { + if (!visible) return null; + + return ( + + + + + This is an experimental feature of Homarr. Please report any issues to the official Homarr + team. + + + + ); +}; diff --git a/src/components/layout/new-header/search.tsx b/src/components/layout/new-header/search.tsx new file mode 100644 index 000000000..0be7f218c --- /dev/null +++ b/src/components/layout/new-header/search.tsx @@ -0,0 +1,180 @@ +import { Autocomplete, Group, Kbd, Text, Tooltip, useMantineTheme } from '@mantine/core'; +import { useHotkeys } from '@mantine/hooks'; +import { + IconBrandYoutube, + IconDownload, + IconMovie, + IconSearch, + IconWorld, + TablerIconsProps, +} from '@tabler/icons-react'; +import { ReactNode, forwardRef, useMemo, useRef, useState } from 'react'; +import { useConfigContext } from '~/config/provider'; +import { api } from '~/utils/api'; + +export const Search = () => { + const [search, setSearch] = useState(''); + const ref = useRef(null); + useHotkeys([['mod+K', () => ref.current?.focus()]]); + const { data: userWithSettings } = api.user.getWithSettings.useQuery(); + const { config } = useConfigContext(); + const { colors } = useMantineTheme(); + + const apps = useConfigApps(search); + const engines = generateEngines( + search, + userWithSettings?.settings.searchTemplate ?? 'https://www.google.com/search?q=%s' + ).filter( + (engine) => + engine.sort !== 'movie' || config?.apps.some((app) => app.integration.type === engine.value) + ); + const data = [...engines, ...apps]; + + return ( + 768} + rightSection={ + ref.current?.focus()} + color={colors.gray[5]} + size={16} + stroke={1.5} + /> + } + limit={8} + value={search} + onChange={setSearch} + data={data} + itemComponent={SearchItemComponent} + filter={(value, item: SearchAutoCompleteItem) => + engines.some((engine) => engine.sort === item.sort) || + item.value.toLowerCase().includes(value.trim().toLowerCase()) + } + classNames={{ + input: 'dashboard-header-search-input', + root: 'dashboard-header-search-root', + }} + onItemSubmit={(item: SearchAutoCompleteItem) => { + setSearch(''); + if (item.sort === 'movie') { + // TODO: show movie modal + console.log('movie'); + return; + } + const target = userWithSettings?.settings.openSearchInNewTab ? '_blank' : '_self'; + window.open(item.metaData.url, target); + }} + aria-label="Search" + /> + ); +}; + +const SearchItemComponent = forwardRef( + ({ icon, label, value, sort, ...others }, ref) => { + let Icon = getItemComponent(icon); + + return ( + + + {label} + + ); + } +); + +const getItemComponent = (icon: SearchAutoCompleteItem['icon']) => { + if (typeof icon !== 'string') { + return icon; + } + + return (props: TablerIconsProps) => ( + + ); +}; + +const useConfigApps = (search: string) => { + const { config } = useConfigContext(); + return useMemo(() => { + if (search.trim().length === 0) return []; + const apps = config?.apps.filter((app) => + app.name.toLowerCase().includes(search.toLowerCase()) + ); + return ( + apps?.map((app) => ({ + icon: app.appearance.iconUrl, + label: app.name, + value: app.name, + sort: 'app', + metaData: { + url: app.behaviour.externalUrl, + }, + })) ?? [] + ); + }, [search, config]); +}; + +type SearchAutoCompleteItem = { + icon: ((props: TablerIconsProps) => ReactNode) | string; + label: string; + value: string; +} & ( + | { + sort: 'web' | 'torrent' | 'youtube' | 'app'; + metaData: { + url: string; + }; + } + | { + sort: 'movie'; + } +); +const movieApps = ['overseerr', 'jellyseerr'] as const; +const generateEngines = (searchValue: string, webTemplate: string) => + searchValue.trim().length > 0 + ? ([ + { + icon: IconWorld, + label: `Search for ${searchValue} in the web`, + value: `web`, + sort: 'web', + metaData: { + url: webTemplate.includes('%s') + ? webTemplate.replace('%s', searchValue) + : webTemplate + searchValue, + }, + }, + { + icon: IconDownload, + label: `Search for ${searchValue} torrents`, + value: `torrent`, + sort: 'torrent', + metaData: { + url: `https://www.torrentdownloads.me/search/?search=${searchValue}`, + }, + }, + { + icon: IconBrandYoutube, + label: `Search for ${searchValue} on youtube`, + value: 'youtube', + sort: 'youtube', + metaData: { + url: `https://www.youtube.com/results?search_query=${searchValue}`, + }, + }, + ...movieApps.map( + (name) => + ({ + icon: IconMovie, + label: `Search for ${searchValue} on ${name}`, + value: name, + sort: 'movie', + }) as const + ), + ] as const satisfies Readonly) + : []; diff --git a/src/pages/index.tsx b/src/pages/index.tsx index d268021ae..80e415deb 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,6 +1,7 @@ import { getCookie, setCookie } from 'cookies-next'; import fs from 'fs'; import { GetServerSidePropsContext } from 'next'; +import { MainLayout } from '~/components/layout/main'; import { LoadConfigComponent } from '../components/Config/LoadConfig'; import { Dashboard } from '../components/Dashboard/Dashboard'; @@ -62,9 +63,9 @@ export default function HomePage({ config: initialConfig }: DashboardServerSideP useInitConfig(initialConfig); return ( - + - +
); } diff --git a/src/server/api/routers/overseerr.ts b/src/server/api/routers/overseerr.ts index 3809d5d1e..0e3545425 100644 --- a/src/server/api/routers/overseerr.ts +++ b/src/server/api/routers/overseerr.ts @@ -3,6 +3,7 @@ import axios from 'axios'; import Consola from 'consola'; import { z } from 'zod'; import { MovieResult } from '~/modules/overseerr/Movie'; +import { Result } from '~/modules/overseerr/SearchResult'; import { TvShowResult } from '~/modules/overseerr/TvShow'; import { getConfig } from '~/tools/config/getConfig'; @@ -14,6 +15,7 @@ export const overseerrRouter = createTRPCRouter({ z.object({ configName: z.string(), query: z.string().or(z.undefined()), + limit: z.number().default(10), }) ) .query(async ({ input }) => { @@ -42,8 +44,9 @@ export const overseerrRouter = createTRPCRouter({ 'X-Api-Key': apiKey, }, }) - .then((res) => res.data); - return data; + .then((res) => res.data as Result[]); + + return data.slice(0, input.limit); }), byId: publicProcedure .input( From cf12c8575db447da730b4dd4179bb420395c495a Mon Sep 17 00:00:00 2001 From: Meier Lukas Date: Sat, 29 Jul 2023 20:56:20 +0200 Subject: [PATCH 019/233] =?UTF-8?q?=E2=9C=A8=20Add=20board=20pages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/b/[id].tsx | 1 + src/pages/b/index.tsx | 1 + src/pages/board/[id].tsx | 16 ++++++++++++++++ src/pages/board/index.tsx | 18 ++++++++++++++++++ 4 files changed, 36 insertions(+) create mode 100644 src/pages/b/[id].tsx create mode 100644 src/pages/b/index.tsx create mode 100644 src/pages/board/[id].tsx create mode 100644 src/pages/board/index.tsx diff --git a/src/pages/b/[id].tsx b/src/pages/b/[id].tsx new file mode 100644 index 000000000..28fd00146 --- /dev/null +++ b/src/pages/b/[id].tsx @@ -0,0 +1 @@ +export { default, getServerSideProps } from '../board/[id]'; diff --git a/src/pages/b/index.tsx b/src/pages/b/index.tsx new file mode 100644 index 000000000..364e8a44f --- /dev/null +++ b/src/pages/b/index.tsx @@ -0,0 +1 @@ +export { default, getServerSideProps } from '../board'; diff --git a/src/pages/board/[id].tsx b/src/pages/board/[id].tsx new file mode 100644 index 000000000..788b14bd6 --- /dev/null +++ b/src/pages/board/[id].tsx @@ -0,0 +1,16 @@ +import { GetServerSideProps } from 'next'; + +export default function BoardPage() { + return ( +
+

BoardPage

+
+ ); +} + +export const getServerSideProps: GetServerSideProps = async () => { + console.log('getServerSideProps'); + return { + props: {}, + }; +}; diff --git a/src/pages/board/index.tsx b/src/pages/board/index.tsx new file mode 100644 index 000000000..615f6dfed --- /dev/null +++ b/src/pages/board/index.tsx @@ -0,0 +1,18 @@ +import { Title } from '@mantine/core'; +import { GetServerSideProps } from 'next'; +import { MainLayout } from '~/components/layout/main'; + +export default function BoardPage() { + return ( + + BoardPage + + ); +} + +export const getServerSideProps: GetServerSideProps = async () => { + console.log('getServerSideProps'); + return { + props: {}, + }; +}; From b4c188e797acaff1ca7f92b7363d3ff990ccfe82 Mon Sep 17 00:00:00 2001 From: Manuel Date: Sat, 29 Jul 2023 21:11:52 +0200 Subject: [PATCH 020/233] =?UTF-8?q?=E2=9C=A8=20Add=20procedure=20for=20reg?= =?UTF-8?q?istration=20tokens=20management?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../create-registration-token.modal.tsx | 72 +++++++++++++++ src/modals/modals.ts | 2 + src/pages/manage/users/index.tsx | 15 ++-- src/pages/manage/users/invites.tsx | 90 ++++++++++++++++++- src/server/api/root.ts | 2 + src/server/api/routers/registrationTokens.ts | 52 +++++++++++ src/validations/registration-token.ts | 9 ++ 7 files changed, 232 insertions(+), 10 deletions(-) create mode 100644 src/modals/create-registration-token/create-registration-token.modal.tsx create mode 100644 src/server/api/routers/registrationTokens.ts create mode 100644 src/validations/registration-token.ts diff --git a/src/modals/create-registration-token/create-registration-token.modal.tsx b/src/modals/create-registration-token/create-registration-token.modal.tsx new file mode 100644 index 000000000..ffc65a6cf --- /dev/null +++ b/src/modals/create-registration-token/create-registration-token.modal.tsx @@ -0,0 +1,72 @@ +import { Button, Group, Stack, Text } from '@mantine/core'; +import { DateInput } from '@mantine/dates'; +import { useForm, zodResolver } from '@mantine/form'; +import { ContextModalProps, modals } from '@mantine/modals'; +import dayjs from 'dayjs'; +import { api } from '~/utils/api'; +import { useI18nZodResolver } from '~/utils/i18n-zod-resolver'; +import { createRegistrationTokenSchema } from '~/validations/registration-token'; + +export const CreateRegistrationTokenModal = ({ + context, + id, + innerProps, +}: ContextModalProps<{}>) => { + const apiContext = api.useContext(); + const { isLoading, mutateAsync } = api.registrationTokens.createRegistrationToken.useMutation({ + onSuccess: async () => { + await apiContext.registrationTokens.getAllInvites.invalidate(); + modals.close(id); + }, + }); + + const { i18nZodResolver } = useI18nZodResolver(); + + const minDate = dayjs().add(5, 'minutes').toDate(); + const maxDate = dayjs().add(6, 'months').toDate(); + + const form = useForm({ + initialValues: { + expirationDate: dayjs().add(7, 'days').toDate(), + }, + validate: i18nZodResolver(createRegistrationTokenSchema), + }); + + return ( + + + After the expiration, a token will no longer be valid and the recipient of the token won't + be able to create an account. + + + + + + + + + + ); +}; diff --git a/src/modals/modals.ts b/src/modals/modals.ts index c5ace5220..f58eba345 100644 --- a/src/modals/modals.ts +++ b/src/modals/modals.ts @@ -7,6 +7,7 @@ import { WidgetsRemoveModal } from '~/components/Dashboard/Tiles/Widgets/Widgets import { CategoryEditModal } from '~/components/Dashboard/Wrappers/Category/CategoryEditModal'; import { DeleteUserModal } from './delete-user/delete-user.modal'; +import { CreateRegistrationTokenModal } from './create-registration-token/create-registration-token.modal'; export const modals = { editApp: EditAppModal, @@ -17,6 +18,7 @@ export const modals = { changeAppPositionModal: ChangeAppPositionModal, changeIntegrationPositionModal: ChangeWidgetPositionModal, deleteUserModal: DeleteUserModal, + createRegistrationTokenModal: CreateRegistrationTokenModal }; declare module '@mantine/modals' { diff --git a/src/pages/manage/users/index.tsx b/src/pages/manage/users/index.tsx index f4f4c0207..4646bd9e8 100644 --- a/src/pages/manage/users/index.tsx +++ b/src/pages/manage/users/index.tsx @@ -29,8 +29,6 @@ const ManageUsersPage = () => { } ); - const { mutateAsync: deleteUserMutateAsync } = api.user.deleteUser.useMutation(); - const [activePage, _] = useState(0); return ( @@ -39,7 +37,10 @@ const ManageUsersPage = () => { Users • Homarr - Manage users + Manage users + + Using users, you have granular control who can access, edit or delete resources on your Homarr instance. + { - {data.pages[activePage].users.map((user) => ( - + {data.pages[activePage].users.map((user, index) => ( + @@ -90,8 +91,8 @@ const ManageUsersPage = () => { title: Delete user ${user.name}, innerProps: { userId: user.id, - username: user.name ?? '' - } + username: user.name ?? '', + }, }); }} color="red" diff --git a/src/pages/manage/users/invites.tsx b/src/pages/manage/users/invites.tsx index 8c0762a23..e88a037d8 100644 --- a/src/pages/manage/users/invites.tsx +++ b/src/pages/manage/users/invites.tsx @@ -1,14 +1,98 @@ -import { Title } from '@mantine/core'; -import { Head } from 'next/document'; +import { ActionIcon, Button, Center, Flex, Pagination, Table, Text, Title } from '@mantine/core'; +import { modals } from '@mantine/modals'; +import { IconPlus, IconTrash } from '@tabler/icons-react'; +import dayjs from 'dayjs'; +import Head from 'next/head'; +import { useState } from 'react'; import { MainLayout } from '~/components/layout/admin/main-admin.layout'; +import { modals as applicationModals } from '~/modals/modals'; +import { api } from '~/utils/api'; const ManageUserInvitesPage = () => { + const { data, isFetched, fetchPreviousPage, fetchNextPage } = + api.registrationTokens.getAllInvites.useInfiniteQuery({ + limit: 10, + }); + + const [activePage, _] = useState(0); + return ( User invites • Homarr - Manage user invites + Manage user invites + + Using registration tokens, you can invite users to your Homarr instance. An invitation will + only be valid for a certain time-span and can be used once. The expiration must be between 5 + minutes and 12 months upon creation. + + + + + + + {data && ( + <> + + + + + + + + + + {data.pages[activePage].registrationTokens.map((token, index) => ( + + + + + + ))} + {data.pages[activePage].registrationTokens.length === 0 && ( + + + + )} + +
IDExpiresActions
+ {token.id} + + {dayjs(dayjs()).isAfter(token.expires) ? ( + expired {dayjs(token.expires).fromNow()} + ) : ( + in {dayjs(token.expires).fromNow(true)} + )} + + {}} color="red" variant="light"> + + +
+
+ There are no invitations yet. +
+
+ + + )}
); }; diff --git a/src/server/api/root.ts b/src/server/api/root.ts index 8f261c685..850554714 100644 --- a/src/server/api/root.ts +++ b/src/server/api/root.ts @@ -15,6 +15,7 @@ import { rssRouter } from './routers/rss'; import { usenetRouter } from './routers/usenet/router'; import { userRouter } from './routers/user'; import { weatherRouter } from './routers/weather'; +import { inviteRouter } from './routers/registrationTokens'; /** * This is the primary router for your server. @@ -37,6 +38,7 @@ export const rootRouter = createTRPCRouter({ usenet: usenetRouter, calendar: calendarRouter, weather: weatherRouter, + registrationTokens: inviteRouter }); // export type definition of API diff --git a/src/server/api/routers/registrationTokens.ts b/src/server/api/routers/registrationTokens.ts new file mode 100644 index 000000000..5ce1c345d --- /dev/null +++ b/src/server/api/routers/registrationTokens.ts @@ -0,0 +1,52 @@ +import dayjs from 'dayjs'; +import { z } from 'zod'; + +import { createTRPCRouter, publicProcedure } from '../trpc'; +import { randomBytes } from 'crypto'; + +export const inviteRouter = createTRPCRouter({ + getAllInvites: publicProcedure + .input( + z.object({ + limit: z.number().min(1).max(100).nullish(), + cursor: z.string().nullish(), + }) + ) + .query(async ({ ctx, input }) => { + const limit = input.limit ?? 50; + const cursor = input.cursor; + const registrationTokens = await ctx.prisma.registrationToken.findMany({ + take: limit + 1, // get an extra item at the end which we'll use as next cursor + cursor: cursor ? { id: cursor } : undefined, + }); + + let nextCursor: typeof cursor | undefined = undefined; + if (registrationTokens.length > limit) { + const nextItem = registrationTokens.pop(); + nextCursor = nextItem!.id; + } + + return { + registrationTokens: registrationTokens.map((token) => ({ + id: token.id, + expires: token.expires, + })), + nextCursor, + }; + }), + createRegistrationToken: publicProcedure.input( + z.object({ + expiration: z + .date() + .min(dayjs().add(5, 'minutes').toDate()) + .max(dayjs().add(6, 'months').toDate()), + }) + ).mutation(async ({ ctx, input }) => { + await ctx.prisma.registrationToken.create({ + data: { + expires: input.expiration, + token: randomBytes(20).toString('hex'), + } + }); + }), +}); diff --git a/src/validations/registration-token.ts b/src/validations/registration-token.ts new file mode 100644 index 000000000..2964ba471 --- /dev/null +++ b/src/validations/registration-token.ts @@ -0,0 +1,9 @@ +import dayjs from 'dayjs'; +import { z } from 'zod'; + +export const createRegistrationTokenSchema = z.object({ + expiration: z + .date() + .min(dayjs().add(5, 'minutes').toDate()) + .max(dayjs().add(6, 'months').toDate()), +}); From 2db3d1405bbea20dfcdfde2c137cbd613090d5e4 Mon Sep 17 00:00:00 2001 From: Meier Lukas Date: Sat, 29 Jul 2023 21:15:55 +0200 Subject: [PATCH 021/233] =?UTF-8?q?=E2=9C=A8=20Implement=20account=20butto?= =?UTF-8?q?n=20for=20new=20header?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Modals/AboutModal/AboutModal.tsx | 2 +- .../layout/new-header/AvatarMenu.tsx | 82 +++++++++++++++++++ src/components/layout/new-header/Header.tsx | 18 +--- 3 files changed, 85 insertions(+), 17 deletions(-) create mode 100644 src/components/layout/new-header/AvatarMenu.tsx diff --git a/src/components/Dashboard/Modals/AboutModal/AboutModal.tsx b/src/components/Dashboard/Modals/AboutModal/AboutModal.tsx index bc3ae16a0..c8f22cd33 100644 --- a/src/components/Dashboard/Modals/AboutModal/AboutModal.tsx +++ b/src/components/Dashboard/Modals/AboutModal/AboutModal.tsx @@ -206,7 +206,7 @@ const useInformationTableItems = (newVersionAvailable?: string): InformationTabl let items: InformationTableItem[] = []; - if (i18n !== null) { + if (i18n?.reportNamespaces) { const usedI18nNamespaces = i18n.reportNamespaces.getUsedNamespaces(); const initOptions = i18n.options as ExtendedInitOptions; diff --git a/src/components/layout/new-header/AvatarMenu.tsx b/src/components/layout/new-header/AvatarMenu.tsx new file mode 100644 index 000000000..83c9377a8 --- /dev/null +++ b/src/components/layout/new-header/AvatarMenu.tsx @@ -0,0 +1,82 @@ +import { Avatar, Badge, Menu, UnstyledButton, useMantineTheme } from '@mantine/core'; +import { useDisclosure } from '@mantine/hooks'; +import { + IconDashboard, + IconInfoCircle, + IconLogin, + IconLogout, + IconSun, + IconUserSearch, +} from '@tabler/icons-react'; +import { User } from 'next-auth'; +import { signOut, useSession } from 'next-auth/react'; +import Link from 'next/link'; +import { forwardRef } from 'react'; +import { AboutModal } from '~/components/Dashboard/Modals/AboutModal/AboutModal'; + +export const AvatarMenu = () => { + const newVersionAvailable = '0.13.0'; + const [aboutModalOpened, aboutModal] = useDisclosure(false); + const { data: sessionData } = useSession(); + + return ( + <> + + + + + + + }>Switch theme + }>View Profile + }>Default Dashboard + + } + rightSection={ + newVersionAvailable && ( + + New + + ) + } + onClick={() => aboutModal.open()} + > + About + + {sessionData?.user ? ( + } color="red" onClick={() => signOut()}> + Logout + + ) : ( + } component={Link} href="/auth/login"> + Login + + )} + + + + + + + ); +}; + +type CurrentUserAvatarProps = { + user: User | null; +}; +const CurrentUserAvatar = forwardRef( + ({ user, ...others }, ref) => { + const { primaryColor } = useMantineTheme(); + if (!user) return ; + return ( + + {user.name?.slice(0, 2).toUpperCase()} + + ); + } +); diff --git a/src/components/layout/new-header/Header.tsx b/src/components/layout/new-header/Header.tsx index 5ac92e1fc..eb0796ee2 100644 --- a/src/components/layout/new-header/Header.tsx +++ b/src/components/layout/new-header/Header.tsx @@ -21,6 +21,7 @@ import { signOut } from 'next-auth/react'; import Link from 'next/link'; import { Logo } from '../Logo'; +import { AvatarMenu } from './AvatarMenu'; import { Search } from './search'; type MainHeaderProps = { @@ -41,22 +42,7 @@ export const MainHeader = ({ showExperimental = false, logoHref = '/' }: MainHea - - - - - - - }>Switch theme - }>View Profile - }>Default Dashboard - - } color="red" onClick={() => signOut()}> - Logout - - - - +
From 698dab032fdd7a1660fa90b839a46af3d6cd71b0 Mon Sep 17 00:00:00 2001 From: Meier Lukas Date: Sat, 29 Jul 2023 21:18:41 +0200 Subject: [PATCH 022/233] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Improve=20avatar?= =?UTF-8?q?=20menu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/layout/new-header/AvatarMenu.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/components/layout/new-header/AvatarMenu.tsx b/src/components/layout/new-header/AvatarMenu.tsx index 83c9377a8..250c709d9 100644 --- a/src/components/layout/new-header/AvatarMenu.tsx +++ b/src/components/layout/new-header/AvatarMenu.tsx @@ -28,9 +28,13 @@ export const AvatarMenu = () => { }>Switch theme - }>View Profile - }>Default Dashboard - + {sessionData?.user && ( + <> + }>View Profile + }>Default Dashboard + + + )} } rightSection={ From cb0b8869e25ee40adadcc09a29686ff5d59d81e9 Mon Sep 17 00:00:00 2001 From: Manuel Date: Sat, 29 Jul 2023 21:21:21 +0200 Subject: [PATCH 023/233] =?UTF-8?q?=E2=9C=A8=20Add=20deletion=20for=20regi?= =?UTF-8?q?stration=20tokens?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../delete-registration-token.modal.tsx | 45 +++++++++++++++++++ src/modals/modals.ts | 4 +- src/pages/manage/users/invites.tsx | 10 ++++- src/server/api/routers/registrationTokens.ts | 43 +++++++++++------- 4 files changed, 84 insertions(+), 18 deletions(-) create mode 100644 src/modals/delete-registration-token/delete-registration-token.modal.tsx diff --git a/src/modals/delete-registration-token/delete-registration-token.modal.tsx b/src/modals/delete-registration-token/delete-registration-token.modal.tsx new file mode 100644 index 000000000..d3ff0fd0b --- /dev/null +++ b/src/modals/delete-registration-token/delete-registration-token.modal.tsx @@ -0,0 +1,45 @@ +import { Button, Group, Stack, Text } from '@mantine/core'; +import { ContextModalProps, modals } from '@mantine/modals'; +import { api } from '~/utils/api'; + +export const DeleteRegistrationTokenModal = ({ + context, + id, + innerProps, +}: ContextModalProps<{ tokenId: string }>) => { + const apiContext = api.useContext(); + const { isLoading, mutateAsync } = api.registrationTokens.deleteRegistrationToken.useMutation({ + onSuccess: async () => { + await apiContext.registrationTokens.getAllInvites.invalidate(); + modals.close(id); + }, + }); + return ( + + + Are you sure, that you want to delete this invitation? Users with this link will no longer + be able to register using that link. + + + + + + + + ); +}; diff --git a/src/modals/modals.ts b/src/modals/modals.ts index f58eba345..6dad48ffe 100644 --- a/src/modals/modals.ts +++ b/src/modals/modals.ts @@ -8,6 +8,7 @@ import { CategoryEditModal } from '~/components/Dashboard/Wrappers/Category/Cate import { DeleteUserModal } from './delete-user/delete-user.modal'; import { CreateRegistrationTokenModal } from './create-registration-token/create-registration-token.modal'; +import { DeleteRegistrationTokenModal } from './delete-registration-token/delete-registration-token.modal'; export const modals = { editApp: EditAppModal, @@ -18,7 +19,8 @@ export const modals = { changeAppPositionModal: ChangeAppPositionModal, changeIntegrationPositionModal: ChangeWidgetPositionModal, deleteUserModal: DeleteUserModal, - createRegistrationTokenModal: CreateRegistrationTokenModal + createRegistrationTokenModal: CreateRegistrationTokenModal, + deleteRegistrationTokenModal: DeleteRegistrationTokenModal }; declare module '@mantine/modals' { diff --git a/src/pages/manage/users/invites.tsx b/src/pages/manage/users/invites.tsx index e88a037d8..120cff6c6 100644 --- a/src/pages/manage/users/invites.tsx +++ b/src/pages/manage/users/invites.tsx @@ -68,7 +68,15 @@ const ManageUserInvitesPage = () => { )} - {}} color="red" variant="light"> + { + modals.openContextModal({ + modal: 'deleteRegistrationTokenModal', + title: Delete registration token, + innerProps: { + tokenId: token.id, + } + }) + }} color="red" variant="light"> diff --git a/src/server/api/routers/registrationTokens.ts b/src/server/api/routers/registrationTokens.ts index 5ce1c345d..da2667ef7 100644 --- a/src/server/api/routers/registrationTokens.ts +++ b/src/server/api/routers/registrationTokens.ts @@ -1,8 +1,8 @@ +import { randomBytes } from 'crypto'; import dayjs from 'dayjs'; import { z } from 'zod'; import { createTRPCRouter, publicProcedure } from '../trpc'; -import { randomBytes } from 'crypto'; export const inviteRouter = createTRPCRouter({ getAllInvites: publicProcedure @@ -34,19 +34,30 @@ export const inviteRouter = createTRPCRouter({ nextCursor, }; }), - createRegistrationToken: publicProcedure.input( - z.object({ - expiration: z - .date() - .min(dayjs().add(5, 'minutes').toDate()) - .max(dayjs().add(6, 'months').toDate()), - }) - ).mutation(async ({ ctx, input }) => { - await ctx.prisma.registrationToken.create({ - data: { - expires: input.expiration, - token: randomBytes(20).toString('hex'), - } - }); - }), + createRegistrationToken: publicProcedure + .input( + z.object({ + expiration: z + .date() + .min(dayjs().add(5, 'minutes').toDate()) + .max(dayjs().add(6, 'months').toDate()), + }) + ) + .mutation(async ({ ctx, input }) => { + await ctx.prisma.registrationToken.create({ + data: { + expires: input.expiration, + token: randomBytes(20).toString('hex'), + }, + }); + }), + deleteRegistrationToken: publicProcedure + .input(z.object({ tokenId: z.string() })) + .mutation(async ({ ctx, input }) => { + await ctx.prisma.registrationToken.delete({ + where: { + id: input.tokenId, + }, + }); + }), }); From 3d2e90923733a46e8a032f814e1a8da14b2cf251 Mon Sep 17 00:00:00 2001 From: Manuel Date: Sat, 29 Jul 2023 21:33:44 +0200 Subject: [PATCH 024/233] =?UTF-8?q?=F0=9F=9A=B8=20Improve=20mobile=20repon?= =?UTF-8?q?siveness?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/layout/Logo.tsx | 4 +++- src/components/layout/admin/main-admin.layout.tsx | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/layout/Logo.tsx b/src/components/layout/Logo.tsx index 9cc76fb7e..21a4833d9 100644 --- a/src/components/layout/Logo.tsx +++ b/src/components/layout/Logo.tsx @@ -1,4 +1,5 @@ import { Group, Image, Text } from '@mantine/core'; +import { useScreenLargerThan } from '~/hooks/useScreenLargerThan'; import { useConfigContext } from '../../config/provider'; import { usePrimaryGradient } from './useGradient'; @@ -11,6 +12,7 @@ interface LogoProps { export function Logo({ size = 'md', withoutText = false }: LogoProps) { const { config } = useConfigContext(); const primaryGradient = usePrimaryGradient(); + const largerThanMd = useScreenLargerThan('md'); return ( @@ -20,7 +22,7 @@ export function Logo({ size = 'md', withoutText = false }: LogoProps) { alt="Homarr Logo" className="dashboard-header-logo-image" /> - {withoutText ? null : ( + {withoutText || !largerThanMd ? null : ( { }, }} navbar={ - + { - + This is an experimental feature of Homarr. Please report any issues to the official Homarr team. From d13347fd4e4b5c4cd8a9eb7a094320bf064acf73 Mon Sep 17 00:00:00 2001 From: Manuel Date: Sat, 29 Jul 2023 21:44:16 +0200 Subject: [PATCH 025/233] =?UTF-8?q?=E2=99=BF=EF=B8=8F=20Add=20mobile=20dra?= =?UTF-8?q?wer=20for=20navigation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../layout/admin/main-admin.layout.tsx | 302 ++++++++++-------- 1 file changed, 163 insertions(+), 139 deletions(-) diff --git a/src/components/layout/admin/main-admin.layout.tsx b/src/components/layout/admin/main-admin.layout.tsx index 5c1ebe4aa..5d812b8b3 100644 --- a/src/components/layout/admin/main-admin.layout.tsx +++ b/src/components/layout/admin/main-admin.layout.tsx @@ -2,6 +2,8 @@ import { AppShell, Avatar, Box, + Burger, + Drawer, Flex, Footer, Group, @@ -16,6 +18,7 @@ import { UnstyledButton, useMantineTheme, } from '@mantine/core'; +import { useDisclosure } from '@mantine/hooks'; import { IconAlertTriangle, IconBook2, @@ -37,6 +40,7 @@ import { useTranslation } from 'next-i18next'; import Image from 'next/image'; import Link from 'next/link'; import { ReactNode } from 'react'; +import { useScreenLargerThan } from '~/hooks/useScreenLargerThan'; import { usePackageAttributesStore } from '~/tools/client/zustands/usePackageAttributesStore'; import { Logo } from '../Logo'; @@ -49,146 +53,166 @@ export const MainLayout = ({ children }: MainLayoutProps) => { const { t } = useTranslation(); const { attributes } = usePackageAttributesStore(); const theme = useMantineTheme(); - return ( - - - - - - } - label="Home" - component={Link} - href="/manage/" - /> - - - - } - > - } - label="Manage" - component={Link} - href="/manage/users" - /> - } - label="Invites" - component={Link} - href="/manage/users/invites" - /> - - - - - } - > - } - component="a" - href="https://homarr.dev/docs/about" - label="Documentation" - /> - } - component="a" - href="https://github.com/ajnart/homarr/issues/new/choose" - label="Report an issue / bug" - /> - } - component="a" - href="https://discord.com/invite/aCsmEV5RgA" - label="Community Discord" - /> - } - component="a" - href="https://github.com/ajnart/homarr" - label="Contribute" - /> - - - - } - header={ -
- - - - - This is an experimental feature of Homarr. Please report any issues to the official - Homarr team. - - - - - - - - - - - - - - - - }>Switch theme - }>View Profile - }>Default Dashboard - - } - color="red" - onClick={() => signOut()} - > - Logout - - - - - - -
- } - footer={ -
- - - - - Homarr - - {attributes.packageVersion && ( - - {attributes.packageVersion} + const screenLargerThanMd = useScreenLargerThan('md'); + + const [burgerMenuOpen, { toggle: toggleBurgerMenu, close: closeBurgerMenu }] = useDisclosure(false); + + const navigationLinks = ( + <> + + + + } + label="Home" + component={Link} + href="/manage/" + /> + + + + } + > + } + label="Manage" + component={Link} + href="/manage/users" + /> + } + label="Invites" + component={Link} + href="/manage/users/invites" + /> + + + + + } + > + } + component="a" + href="https://homarr.dev/docs/about" + label="Documentation" + /> + } + component="a" + href="https://github.com/ajnart/homarr/issues/new/choose" + label="Report an issue / bug" + /> + } + component="a" + href="https://discord.com/invite/aCsmEV5RgA" + label="Community Discord" + /> + } + component="a" + href="https://github.com/ajnart/homarr" + label="Contribute" + /> + + + ); + return ( + <> +
- } - > - - {children} - - + + + + + {!screenLargerThanMd && ( + + )} + + + + + + + + + + + + + + }>Switch theme + }>View Profile + }>Default Dashboard + + } + color="red" + onClick={() => signOut()} + > + Logout + + + + + + +
+ } + footer={ +
+ + + + + Homarr + + {attributes.packageVersion && ( + + {attributes.packageVersion} + + )} + + +
+ } + > + + {children} + + + + {navigationLinks} + + ); }; From 5e873cad3fb231bbc4d50e4fa4fea8a01cec245c Mon Sep 17 00:00:00 2001 From: Manuel Date: Sat, 29 Jul 2023 21:58:36 +0200 Subject: [PATCH 026/233] =?UTF-8?q?=E2=99=BF=EF=B8=8F=20Improve=20manageme?= =?UTF-8?q?nt=20start=20page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/manage/index.tsx | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/pages/manage/index.tsx b/src/pages/manage/index.tsx index 7e1b52238..0de53cc5b 100644 --- a/src/pages/manage/index.tsx +++ b/src/pages/manage/index.tsx @@ -12,26 +12,29 @@ import { import { IconArrowRight } from '@tabler/icons-react'; import Image from 'next/image'; import { MainLayout } from '~/components/layout/admin/main-admin.layout'; +import { useScreenLargerThan } from '~/hooks/useScreenLargerThan'; const ManagementPage = () => { const { classes } = useStyles(); + const largerThanMd = useScreenLargerThan('md'); return ( - - + + Welcome back, Manicraft1001 - - Are you ready to organize? -
- You currently have 3 dashboards with 39 apps on them. -
+ Welcome to Your Application Hub. Organize, Optimize, and Conquer!
- - + +
From 588ad4313a0eadb8823a05411fa702736f803f04 Mon Sep 17 00:00:00 2001 From: Meier Lukas Date: Sat, 29 Jul 2023 22:06:21 +0200 Subject: [PATCH 027/233] =?UTF-8?q?=E2=9C=A8=20Add=20color=20scheme=20togg?= =?UTF-8?q?le=20for=20new=20header?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/layout/new-header/AvatarMenu.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/components/layout/new-header/AvatarMenu.tsx b/src/components/layout/new-header/AvatarMenu.tsx index 250c709d9..7cf10347a 100644 --- a/src/components/layout/new-header/AvatarMenu.tsx +++ b/src/components/layout/new-header/AvatarMenu.tsx @@ -5,6 +5,7 @@ import { IconInfoCircle, IconLogin, IconLogout, + IconMoonStars, IconSun, IconUserSearch, } from '@tabler/icons-react'; @@ -13,11 +14,15 @@ import { signOut, useSession } from 'next-auth/react'; import Link from 'next/link'; import { forwardRef } from 'react'; import { AboutModal } from '~/components/Dashboard/Modals/AboutModal/AboutModal'; +import { useColorScheme } from '~/hooks/use-colorscheme'; export const AvatarMenu = () => { const newVersionAvailable = '0.13.0'; const [aboutModalOpened, aboutModal] = useDisclosure(false); const { data: sessionData } = useSession(); + const { colorScheme, toggleColorScheme } = useColorScheme(); + + const Icon = colorScheme === 'dark' ? IconSun : IconMoonStars; return ( <> @@ -27,7 +32,9 @@ export const AvatarMenu = () => { - }>Switch theme + } onClick={toggleColorScheme}> + Switch theme + {sessionData?.user && ( <> }>View Profile From f84d9ed7d43b2bedce18d614601583f402aaf78d Mon Sep 17 00:00:00 2001 From: Meier Lukas Date: Sat, 29 Jul 2023 23:03:40 +0200 Subject: [PATCH 028/233] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Improve=20avatar?= =?UTF-8?q?=20menu,=20add=20avatar=20menu=20to=20manage=20pages,=20add=20d?= =?UTF-8?q?efault=20dashboard=20to=20user=20settings=20schema?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prisma/schema.prisma | 1 + .../layout/admin/main-admin.layout.tsx | 52 ++----------------- .../layout/new-header/AvatarMenu.tsx | 14 +++-- 3 files changed, 16 insertions(+), 51 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index ed932d718..de378532c 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -74,6 +74,7 @@ model UserSettings { userId String colorScheme String @default("environment") // environment, light, dark language String @default("en") + defaultDashboard String @default("default") searchTemplate String @default("https://google.com/search?q=%s") openSearchInNewTab Boolean @default(true) disablePingPulse Boolean @default(false) diff --git a/src/components/layout/admin/main-admin.layout.tsx b/src/components/layout/admin/main-admin.layout.tsx index 5d812b8b3..619e3ab0c 100644 --- a/src/components/layout/admin/main-admin.layout.tsx +++ b/src/components/layout/admin/main-admin.layout.tsx @@ -44,6 +44,7 @@ import { useScreenLargerThan } from '~/hooks/useScreenLargerThan'; import { usePackageAttributesStore } from '~/tools/client/zustands/usePackageAttributesStore'; import { Logo } from '../Logo'; +import { MainHeader } from '../new-header/Header'; interface MainLayoutProps { children: ReactNode; @@ -56,7 +57,8 @@ export const MainLayout = ({ children }: MainLayoutProps) => { const screenLargerThanMd = useScreenLargerThan('md'); - const [burgerMenuOpen, { toggle: toggleBurgerMenu, close: closeBurgerMenu }] = useDisclosure(false); + const [burgerMenuOpen, { toggle: toggleBurgerMenu, close: closeBurgerMenu }] = + useDisclosure(false); const navigationLinks = ( <> @@ -141,53 +143,7 @@ export const MainLayout = ({ children }: MainLayoutProps) => { } - header={ -
- - - - - This is an experimental feature of Homarr. Please report any issues to the - official Homarr team. - - - - - - {!screenLargerThanMd && ( - - )} - - - - - - - - - - - - - - }>Switch theme - }>View Profile - }>Default Dashboard - - } - color="red" - onClick={() => signOut()} - > - Logout - - - - - - -
- } + header={} footer={