diff --git a/package.json b/package.json index 21da14528..aeea86ea4 100644 --- a/package.json +++ b/package.json @@ -120,7 +120,7 @@ "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", "@vitest/coverage-c8": "^0.33.0", - "@vitest/ui": "^0.33.0", + "@vitest/ui": "^0.34.4", "eslint": "^8.0.1", "eslint-config-next": "^13.4.5", "eslint-plugin-promise": "^6.0.0", diff --git a/public/locales/en/settings/customization/access.json b/public/locales/en/settings/customization/access.json new file mode 100644 index 000000000..1d49bfc83 --- /dev/null +++ b/public/locales/en/settings/customization/access.json @@ -0,0 +1,6 @@ +{ + "allowGuests": { + "label": "Allow anonymous", + "description": "Allow users that are not logged in to view your board" + } +} \ No newline at end of file diff --git a/public/locales/en/settings/customization/general.json b/public/locales/en/settings/customization/general.json index 358b5158b..adbbfadaf 100644 --- a/public/locales/en/settings/customization/general.json +++ b/public/locales/en/settings/customization/general.json @@ -20,6 +20,10 @@ "accessibility": { "name": "Accessibility", "description": "Configure Homarr for disabled and handicapped users" + }, + "access": { + "name": "Acccess", + "description": "Configure who has access to your board" } } } diff --git a/src/components/Board/Customize/Access/AccessCustomization.tsx b/src/components/Board/Customize/Access/AccessCustomization.tsx new file mode 100644 index 000000000..8ff4f3f3b --- /dev/null +++ b/src/components/Board/Customize/Access/AccessCustomization.tsx @@ -0,0 +1,13 @@ +import { Stack, Switch } from '@mantine/core'; +import { useTranslation } from 'next-i18next'; +import { useBoardCustomizationFormContext } from '~/components/Board/Customize/form'; + +export const AccessCustomization = () => { + const { t } = useTranslation('settings/customization/access'); + const form = useBoardCustomizationFormContext(); + return ( + + + + ) +} \ No newline at end of file diff --git a/src/components/Board/Customize/Appearance/AppearanceCustomization.tsx b/src/components/Board/Customize/Appearance/AppearanceCustomization.tsx index 748599d04..248f4e855 100644 --- a/src/components/Board/Customize/Appearance/AppearanceCustomization.tsx +++ b/src/components/Board/Customize/Appearance/AppearanceCustomization.tsx @@ -15,7 +15,6 @@ import { import { useTranslation } from 'next-i18next'; import { highlight, languages } from 'prismjs'; import Editor from 'react-simple-code-editor'; -import { useColorScheme } from '~/hooks/use-colorscheme'; import { useColorTheme } from '~/tools/color'; import { useBoardCustomizationFormContext } from '../form'; diff --git a/src/components/Board/Customize/form.ts b/src/components/Board/Customize/form.ts index eee8c6976..141eb1af4 100644 --- a/src/components/Board/Customize/form.ts +++ b/src/components/Board/Customize/form.ts @@ -1,4 +1,3 @@ -import { DEFAULT_THEME, MANTINE_COLORS, MantineColor } from '@mantine/core'; import { createFormContext } from '@mantine/form'; import { z } from 'zod'; import { boardCustomizationSchema } from '~/validations/boards'; diff --git a/src/pages/board/[slug].tsx b/src/pages/board/[slug].tsx index ebb300a72..f473a3b36 100644 --- a/src/pages/board/[slug].tsx +++ b/src/pages/board/[slug].tsx @@ -10,6 +10,7 @@ import { getFrontendConfig } from '~/tools/config/getFrontendConfig'; import { getServerSideTranslations } from '~/tools/server/getServerSideTranslations'; import { boardNamespaces } from '~/tools/server/translation-namespaces'; import { ConfigType } from '~/types/config'; +import { getServerAuthSession } from '~/server/auth'; export default function BoardPage({ config: initialConfig, @@ -57,14 +58,36 @@ export const getServerSideProps: GetServerSideProps = a const config = await getFrontendConfig(routeParams.data.slug); const translations = await getServerSideTranslations(boardNamespaces, locale, req, res); - return { - props: { - config, - primaryColor: config.settings.customization.colors.primary, - secondaryColor: config.settings.customization.colors.secondary, - primaryShade: config.settings.customization.colors.shade, - dockerEnabled: !!env.DOCKER_HOST && !!env.DOCKER_PORT, - ...translations, - }, - }; + const getSuccessResponse = () => { + return { + props: { + config, + primaryColor: config.settings.customization.colors.primary, + secondaryColor: config.settings.customization.colors.secondary, + primaryShade: config.settings.customization.colors.shade, + dockerEnabled: !!env.DOCKER_HOST && !!env.DOCKER_PORT, + ...translations, + }, + }; + } + + + if (!config.settings.access.allowGuests) { + const session = await getServerAuthSession({ req, res }); + + if (session?.user) { + return getSuccessResponse(); + } + + return { + notFound: true, + props: { + primaryColor: config.settings.customization.colors.primary, + secondaryColor: config.settings.customization.colors.secondary, + primaryShade: config.settings.customization.colors.shade, + } + }; + } + + return getSuccessResponse(); }; diff --git a/src/pages/board/[slug]/customize.tsx b/src/pages/board/[slug]/customize.tsx index 6b23dce0e..b027098be 100644 --- a/src/pages/board/[slug]/customize.tsx +++ b/src/pages/board/[slug]/customize.tsx @@ -18,7 +18,7 @@ import { IconChartCandle, IconCheck, IconDragDrop, - IconLayout, + IconLayout, IconLock, IconX, TablerIconsProps, } from '@tabler/icons-react'; @@ -46,6 +46,7 @@ import { firstUpperCase } from '~/tools/shared/strings'; import { api } from '~/utils/api'; import { useI18nZodResolver } from '~/utils/i18n-zod-resolver'; import { boardCustomizationSchema } from '~/validations/boards'; +import { AccessCustomization } from '~/components/Board/Customize/Access/AccessCustomization'; const notificationId = 'board-customization-notification'; @@ -58,6 +59,9 @@ export default function CustomizationPage() { const { t } = useTranslation('boards/customize'); const form = useBoardCustomizationForm({ initialValues: { + access: { + allowGuests: config?.settings.access.allowGuests ?? false + }, layout: { leftSidebarEnabled: config?.settings.customization.layout.enabledLeftSidebar ?? false, rightSidebarEnabled: config?.settings.customization.layout.enabledRightSidebar ?? false, @@ -210,6 +214,10 @@ export default function CustomizationPage() { + + + + @@ -232,7 +240,7 @@ export default function CustomizationPage() { } type SectionTitleProps = { - type: 'layout' | 'gridstack' | 'pageMetadata' | 'appereance'; + type: 'layout' | 'gridstack' | 'pageMetadata' | 'appereance' | 'access'; icon: (props: TablerIconsProps) => ReactNode; }; @@ -282,6 +290,7 @@ export const getServerSideProps: GetServerSideProps = async ({ req, res, locale, 'settings/customization/shade-selector', 'settings/customization/opacity-selector', 'settings/customization/gridstack', + 'settings/customization/access' ], locale, req, diff --git a/src/pages/board/index.tsx b/src/pages/board/index.tsx index 8bb2ae230..3736c31dc 100644 --- a/src/pages/board/index.tsx +++ b/src/pages/board/index.tsx @@ -47,6 +47,17 @@ export const getServerSideProps: GetServerSideProps = a const boardName = currentUserSettings?.defaultBoard ?? 'default'; const config = await getFrontendConfig(boardName); + if (!config.settings.access.allowGuests && !session?.user) { + return { + notFound: true, + props: { + primaryColor: config.settings.customization.colors.primary, + secondaryColor: config.settings.customization.colors.secondary, + primaryShade: config.settings.customization.colors.shade, + } + }; + } + return { props: { config, diff --git a/src/server/api/routers/config.ts b/src/server/api/routers/config.ts index a7e0e063e..25b22bc83 100644 --- a/src/server/api/routers/config.ts +++ b/src/server/api/routers/config.ts @@ -191,6 +191,10 @@ export const configRouter = createTRPCRouter({ ...previousConfig, settings: { ...previousConfig.settings, + access: { + ...previousConfig.settings.access, + allowGuests: input.access.allowGuests, + }, customization: { ...previousConfig.settings.customization, appOpacity: input.appearance.opacity, diff --git a/src/tools/config/getConfig.ts b/src/tools/config/getConfig.ts index 1866a5480..8659c61d5 100644 --- a/src/tools/config/getConfig.ts +++ b/src/tools/config/getConfig.ts @@ -36,5 +36,11 @@ export const getConfig = (name: string): BackendConfigType => { writeConfig(backendConfig); } + if (!backendConfig.settings.access) { + backendConfig.settings.access = { + allowGuests: false, + }; + } + return backendConfig; }; diff --git a/src/tools/config/getFallbackConfig.ts b/src/tools/config/getFallbackConfig.ts index 5cc72bf09..d5ca11750 100644 --- a/src/tools/config/getFallbackConfig.ts +++ b/src/tools/config/getFallbackConfig.ts @@ -11,7 +11,7 @@ export const getFallbackConfig = (name?: string) => ({ export const getStaticFallbackConfig = (name: string): ConfigType => ({ schemaVersion: 1, configProperties: { - name: name, + name: name }, categories: [ { @@ -33,6 +33,9 @@ export const getStaticFallbackConfig = (name: string): ConfigType => ({ apps: [], widgets: [], settings: { + access: { + allowGuests: false, + }, common: { searchEngine: { type: 'google', diff --git a/src/types/settings.ts b/src/types/settings.ts index d09764d3f..1d4ba5cf2 100644 --- a/src/types/settings.ts +++ b/src/types/settings.ts @@ -3,6 +3,11 @@ import { MantineTheme } from '@mantine/core'; export interface SettingsType { common: CommonSettingsType; customization: CustomizationSettingsType; + access: BoardAccessSettingsType; +} + +export interface BoardAccessSettingsType { + allowGuests: boolean; } export interface CommonSettingsType { diff --git a/src/validations/boards.ts b/src/validations/boards.ts index 35c7e707c..9868e2072 100644 --- a/src/validations/boards.ts +++ b/src/validations/boards.ts @@ -6,6 +6,9 @@ export const createBoardSchemaValidation = z.object({ }); export const boardCustomizationSchema = z.object({ + access: z.object({ + allowGuests: z.boolean() + }), layout: z.object({ leftSidebarEnabled: z.boolean(), rightSidebarEnabled: z.boolean(), diff --git a/tests/pages/board/[slug].spec.ts b/tests/pages/board/[slug].spec.ts new file mode 100644 index 000000000..ad8549026 --- /dev/null +++ b/tests/pages/board/[slug].spec.ts @@ -0,0 +1,172 @@ +import { NextApiRequest, NextApiResponse } from 'next'; + +import { SSRConfig } from 'next-i18next'; + +import { ParsedUrlQuery } from 'querystring'; + +import { describe, expect, it, vitest } from 'vitest'; + +import { getServerSideProps } from '../../../src/pages/board/[slug]'; + +import * as configExistsModule from '../../../src/tools/config/configExists'; +import * as getFrontendConfigModule from '../../../src/tools/config/getFrontendConfig'; +import * as getServerSideTranslationsModule from '../../../src/tools/server/getServerSideTranslations'; + +import * as serverAuthModule from '~/server/auth'; +import { ConfigType } from '~/types/config'; + +vitest.mock('./../../server/auth.ts', () => ({ + getServerAuthSession: () => null, +})); + +vitest.mock('./../../tools/config/getFrontendConfig.ts', () => ({ + getFrontendConfig: (board: string) => null, +})); + +vitest.mock('./../../tools/config/configExists.ts', () => ({ + configExists: (board: string) => null, +})); + +vitest.mock('./../../env.js', () => import.meta); + +vitest.mock('./../../tools/server/getServerSideTranslations.ts', () => ({ + getServerSideTranslations: () => null, +})); + +describe('[slug] page', () => { + it('getServerSideProps should return not found when no params', async () => { + // arrange + vitest.spyOn(configExistsModule, 'configExists').mockReturnValue(false); + vitest + .spyOn(getFrontendConfigModule, 'getFrontendConfig') + .mockReturnValue(Promise.resolve(null as unknown as ConfigType)); + vitest + .spyOn(getServerSideTranslationsModule, 'getServerSideTranslations') + .mockReturnValue(Promise.resolve(null as unknown as SSRConfig)); + vitest.spyOn(serverAuthModule, 'getServerAuthSession').mockReturnValue(Promise.resolve(null)); + + // act + const response = await getServerSideProps({ + req: {} as NextApiRequest, + res: {} as NextApiResponse, + query: {} as ParsedUrlQuery, + resolvedUrl: '/board/testing-board', + }); + + // assert + expect(response).toStrictEqual({ + notFound: true, + }); + expect(configExistsModule.configExists).not.toHaveBeenCalled(); + expect(getFrontendConfigModule.getFrontendConfig).not.toHaveBeenCalled(); + }); + + it('getServerSideProps should return not found when invalid params', async () => { + // arrange + vitest.spyOn(configExistsModule, 'configExists').mockReturnValue(false); + vitest + .spyOn(getFrontendConfigModule, 'getFrontendConfig') + .mockReturnValue(Promise.resolve(null as unknown as ConfigType)); + vitest + .spyOn(getServerSideTranslationsModule, 'getServerSideTranslations') + .mockReturnValue(Promise.resolve(null as unknown as SSRConfig)); + vitest.spyOn(serverAuthModule, 'getServerAuthSession').mockReturnValue(Promise.resolve(null)); + + // act + const response = await getServerSideProps({ + req: {} as NextApiRequest, + res: {} as NextApiResponse, + query: { + test: 'test', + }, + resolvedUrl: '/board/testing-board', + }); + + expect(response).toStrictEqual({ + notFound: true, + }); + expect(configExistsModule.configExists).not.toHaveBeenCalled(); + expect(getFrontendConfigModule.getFrontendConfig).not.toHaveBeenCalled(); + }); + + it('getServerSideProps should return not found when valid params but no config with said name', async () => { + // arrange + vitest.spyOn(configExistsModule, 'configExists').mockReturnValue(false); + vitest + .spyOn(getFrontendConfigModule, 'getFrontendConfig') + .mockReturnValueOnce(Promise.resolve(null as unknown as ConfigType)); + vitest + .spyOn(getServerSideTranslationsModule, 'getServerSideTranslations') + .mockReturnValue(Promise.resolve(null as unknown as SSRConfig)); + vitest.spyOn(serverAuthModule, 'getServerAuthSession').mockReturnValue(Promise.resolve(null)); + + // act + const response = await getServerSideProps({ + req: {} as NextApiRequest, + res: {} as NextApiResponse, + query: {}, + params: { + slug: 'testing-board', + }, + resolvedUrl: '/board/testing-board', + }); + + // assert + expect(response).toStrictEqual({ + notFound: true, + }); + expect(configExistsModule.configExists).toHaveBeenCalledOnce(); + expect(getFrontendConfigModule.getFrontendConfig).not.toHaveBeenCalled(); + }); + + it('getServerSideProps should return when valid params and config', async () => { + // arrange + vitest.spyOn(configExistsModule, 'configExists').mockReturnValue(true); + vitest.spyOn(getFrontendConfigModule, 'getFrontendConfig').mockReturnValueOnce( + Promise.resolve({ + settings: { + access: { + allowGuests: false, + }, + customization: { + colors: { + primary: 'red', + secondary: 'blue', + shade: 'green', + }, + }, + }, + } as unknown as ConfigType) + ); + vitest + .spyOn(serverAuthModule, 'getServerAuthSession') + .mockReturnValueOnce(Promise.resolve(null)); + vitest + .spyOn(getServerSideTranslationsModule, 'getServerSideTranslations') + .mockReturnValue(Promise.resolve(null as unknown as SSRConfig)); + + // act + const response = await getServerSideProps({ + req: {} as NextApiRequest, + res: {} as NextApiResponse, + query: {}, + params: { + slug: 'my-authentication-board', + }, + resolvedUrl: '/board/my-authentication-board', + }); + + // assert + expect(response).toEqual({ + notFound: true, + props: { + primaryColor: 'red', + secondaryColor: 'blue', + primaryShade: 'green', + }, + }); + expect(serverAuthModule.getServerAuthSession).toHaveBeenCalledOnce(); + expect(configExistsModule.configExists).toHaveBeenCalledOnce(); + expect(getFrontendConfigModule.getFrontendConfig).toHaveBeenCalledOnce(); + }); +}); diff --git a/vitest.config.ts b/vitest.config.ts index 86a5337de..b98664c84 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -8,7 +8,7 @@ export default defineConfig({ test: { environment: 'happy-dom', coverage: { - provider: 'c8', + provider: 'v8', reporter: ['html'], all: true, exclude: ['.next/', '.yarn/', 'data/'], diff --git a/yarn.lock b/yarn.lock index ee149e8f6..6bd1d686a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -775,10 +775,10 @@ __metadata: languageName: node linkType: hard -"@eslint/js@npm:^8.47.0": - version: 8.47.0 - resolution: "@eslint/js@npm:8.47.0" - checksum: 0ef57fe27b6d4c305b33f3b2d2fee1ab397a619006f1d6f4ce5ee4746b8f03d11a4e098805a7d78601ca534cf72917d37f0ac19896c992a32e26299ecb9f9de1 +"@eslint/js@npm:8.49.0": + version: 8.49.0 + resolution: "@eslint/js@npm:8.49.0" + checksum: a6601807c8aeeefe866926ad92ed98007c034a735af20ff709009e39ad1337474243d47908500a3bde04e37bfba16bcf1d3452417f962e1345bc8756edd6b830 languageName: node linkType: hard @@ -834,14 +834,14 @@ __metadata: languageName: node linkType: hard -"@humanwhocodes/config-array@npm:^0.11.10": - version: 0.11.10 - resolution: "@humanwhocodes/config-array@npm:0.11.10" +"@humanwhocodes/config-array@npm:^0.11.11": + version: 0.11.11 + resolution: "@humanwhocodes/config-array@npm:0.11.11" dependencies: "@humanwhocodes/object-schema": ^1.2.1 debug: ^4.1.1 minimatch: ^3.0.5 - checksum: 1b1302e2403d0e35bc43e66d67a2b36b0ad1119efc704b5faff68c41f791a052355b010fb2d27ef022670f550de24cd6d08d5ecf0821c16326b7dcd0ee5d5d8a + checksum: db84507375ab77b8ffdd24f498a5b49ad6b64391d30dd2ac56885501d03964d29637e05b1ed5aefa09d57ac667e28028bc22d2da872bfcd619652fbdb5f4ca19 languageName: node linkType: hard @@ -1507,10 +1507,10 @@ __metadata: languageName: node linkType: hard -"@prisma/engines@npm:5.2.0": - version: 5.2.0 - resolution: "@prisma/engines@npm:5.2.0" - checksum: c4d0a424b211ab5f02c977bd87e03a151a7d297d8448b08ef9de931a0dcebbbea76cdefc15a17fd06dacac692b164fd88b32c23eb84f7822dbaf3d0885b700a7 +"@prisma/engines@npm:5.3.1": + version: 5.3.1 + resolution: "@prisma/engines@npm:5.3.1" + checksum: a231adad60ac42569b560ea9782bc181818d8ad15e65283d1317bda5d7aa754e5b536a3f9365ce1eda8961e1eff4eca5978c456fa9764a867fe4339d123e7371 languageName: node linkType: hard @@ -2967,6 +2967,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/scope-manager@npm:6.7.2": + version: 6.7.2 + resolution: "@typescript-eslint/scope-manager@npm:6.7.2" + dependencies: + "@typescript-eslint/types": 6.7.2 + "@typescript-eslint/visitor-keys": 6.7.2 + checksum: e35fa23ecb16252c3ad00b5f1ec05d9b8d33ee30d4c57543892f900443ed77926be9bd2836f06463c31b483f5f0f79070273bc51c4a606f55ac3cd1d9c9cd542 + languageName: node + linkType: hard + "@typescript-eslint/type-utils@npm:6.4.1": version: 6.4.1 resolution: "@typescript-eslint/type-utils@npm:6.4.1" @@ -2998,6 +3008,13 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/types@npm:6.7.2": + version: 6.7.2 + resolution: "@typescript-eslint/types@npm:6.7.2" + checksum: 5a7c4cd456f721649757d2edb4cae71d1405c1c2c35672031f012b27007b9d49b7118297eec746dc3351370e6aa414e5d2c493fb658c7b910154b7998c0278e1 + languageName: node + linkType: hard + "@typescript-eslint/typescript-estree@npm:5.62.0": version: 5.62.0 resolution: "@typescript-eslint/typescript-estree@npm:5.62.0" @@ -3034,7 +3051,25 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/utils@npm:6.4.1, @typescript-eslint/utils@npm:^6.2.0": +"@typescript-eslint/typescript-estree@npm:6.7.2": + version: 6.7.2 + resolution: "@typescript-eslint/typescript-estree@npm:6.7.2" + dependencies: + "@typescript-eslint/types": 6.7.2 + "@typescript-eslint/visitor-keys": 6.7.2 + debug: ^4.3.4 + globby: ^11.1.0 + is-glob: ^4.0.3 + semver: ^7.5.4 + ts-api-utils: ^1.0.1 + peerDependenciesMeta: + typescript: + optional: true + checksum: c30b9803567c37527e2806badd98f3083ae125db9a430d8a28647b153e446e6a4b830833f229cca27d5aa0ff5497c149aaa524aa3a6dbf932b557c60d0bfd4f9 + languageName: node + linkType: hard + +"@typescript-eslint/utils@npm:6.4.1": version: 6.4.1 resolution: "@typescript-eslint/utils@npm:6.4.1" dependencies: @@ -3069,6 +3104,23 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/utils@npm:^6.2.0": + version: 6.7.2 + resolution: "@typescript-eslint/utils@npm:6.7.2" + dependencies: + "@eslint-community/eslint-utils": ^4.4.0 + "@types/json-schema": ^7.0.12 + "@types/semver": ^7.5.0 + "@typescript-eslint/scope-manager": 6.7.2 + "@typescript-eslint/types": 6.7.2 + "@typescript-eslint/typescript-estree": 6.7.2 + semver: ^7.5.4 + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + checksum: 97f950562dba2bda63ffe64672f643ef940123cf74007bc878afcf31c75f905c99934a3ad77da3d5a4fe7807d5d69c791b20c429712ad5a5525e331ebc313756 + languageName: node + linkType: hard + "@typescript-eslint/visitor-keys@npm:5.62.0": version: 5.62.0 resolution: "@typescript-eslint/visitor-keys@npm:5.62.0" @@ -3089,6 +3141,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/visitor-keys@npm:6.7.2": + version: 6.7.2 + resolution: "@typescript-eslint/visitor-keys@npm:6.7.2" + dependencies: + "@typescript-eslint/types": 6.7.2 + eslint-visitor-keys: ^3.4.1 + checksum: b4915fbc0f3d44c81b92b7151830b698e8b6ed2dee8587bb65540c888c7a84300d3fd6b0c159e2131c7c6df1bebe49fb0d21c347ecdbf7f3e4aec05acebbb0bc + languageName: node + linkType: hard + "@videojs/http-streaming@npm:3.5.3": version: 3.5.3 resolution: "@videojs/http-streaming@npm:3.5.3" @@ -3211,11 +3273,11 @@ __metadata: languageName: node linkType: hard -"@vitest/ui@npm:^0.33.0": - version: 0.33.0 - resolution: "@vitest/ui@npm:0.33.0" +"@vitest/ui@npm:^0.34.4": + version: 0.34.4 + resolution: "@vitest/ui@npm:0.34.4" dependencies: - "@vitest/utils": 0.33.0 + "@vitest/utils": 0.34.4 fast-glob: ^3.3.0 fflate: ^0.8.0 flatted: ^3.2.7 @@ -3224,7 +3286,7 @@ __metadata: sirv: ^2.0.3 peerDependencies: vitest: ">=0.30.1 <1" - checksum: 46e9bd9529d9cb8a7c3b9d7e1a4d56ef74a43efd35d33283305e837d411e267ef72b78481546605a605f89962271f02f6c63638941fe542177d00e9d192b1255 + checksum: 64e8a67bd4eafedc659b99d5b64d3b7af981652574622f4f9e2d27d6ff2904b8463fac76b77b9196a70295cefdb0a6dc05e218ef3698218837acdec06a119638 languageName: node linkType: hard @@ -3239,6 +3301,17 @@ __metadata: languageName: node linkType: hard +"@vitest/utils@npm:0.34.4": + version: 0.34.4 + resolution: "@vitest/utils@npm:0.34.4" + dependencies: + diff-sequences: ^29.4.3 + loupe: ^2.3.6 + pretty-format: ^29.5.0 + checksum: 5ec5e9d6de14fff0520a61843f54a90690c10c0cd8d54439d4e9f5ac1508aa27d2c4b78ab332c222ca3199999f0d006cf938fe1a0c63c317c297af12983c5499 + languageName: node + linkType: hard + "@xmldom/xmldom@npm:^0.8.3": version: 0.8.10 resolution: "@xmldom/xmldom@npm:0.8.10" @@ -3693,13 +3766,13 @@ __metadata: linkType: hard "axios@npm:^1.0.0": - version: 1.4.0 - resolution: "axios@npm:1.4.0" + version: 1.5.0 + resolution: "axios@npm:1.5.0" dependencies: follow-redirects: ^1.15.0 form-data: ^4.0.0 proxy-from-env: ^1.1.0 - checksum: 7fb6a4313bae7f45e89d62c70a800913c303df653f19eafec88e56cea2e3821066b8409bc68be1930ecca80e861c52aa787659df0ffec6ad4d451c7816b9386b + checksum: e7405a5dbbea97760d0e6cd58fecba311b0401ddb4a8efbc4108f5537da9b3f278bde566deb777935a960beec4fa18e7b8353881f2f465e4f2c0e949fead35be languageName: node linkType: hard @@ -5883,14 +5956,14 @@ __metadata: linkType: hard "eslint@npm:^8.0.1": - version: 8.47.0 - resolution: "eslint@npm:8.47.0" + version: 8.49.0 + resolution: "eslint@npm:8.49.0" dependencies: "@eslint-community/eslint-utils": ^4.2.0 "@eslint-community/regexpp": ^4.6.1 "@eslint/eslintrc": ^2.1.2 - "@eslint/js": ^8.47.0 - "@humanwhocodes/config-array": ^0.11.10 + "@eslint/js": 8.49.0 + "@humanwhocodes/config-array": ^0.11.11 "@humanwhocodes/module-importer": ^1.0.1 "@nodelib/fs.walk": ^1.2.8 ajv: ^6.12.4 @@ -5925,7 +5998,7 @@ __metadata: text-table: ^0.2.0 bin: eslint: bin/eslint.js - checksum: 1988617f703eadc5c7540468d54dc8e5171cf2bb9483f6172799cd1ff54a9a5e4470f003784e8cef92687eaa14de37172732787040e67817581a20bcb9c15970 + checksum: 4dfe257e1e42da2f9da872b05aaaf99b0f5aa022c1a91eee8f2af1ab72651b596366320c575ccd4e0469f7b4c97aff5bb85ae3323ebd6a293c3faef4028b0d81 languageName: node linkType: hard @@ -6149,9 +6222,9 @@ __metadata: linkType: hard "flag-icons@npm:^6.9.2": - version: 6.11.0 - resolution: "flag-icons@npm:6.11.0" - checksum: 859c4dfa104bbaa3bf49484764e1d9144d644c8acfff581591e925733d5b4731226be065b91ccac4b0e30a49fda6ba3c1468af4d5e35642dcefd03f468040efe + version: 6.11.1 + resolution: "flag-icons@npm:6.11.1" + checksum: 72f93fa261f0c345633af9e106ece7bbabe4de75e3b150509228465ac2a156b8d3b5d9373bc3fe00f4f259b9f24790a4b3617c45fb436b565d794f30a44a7fef languageName: node linkType: hard @@ -6247,8 +6320,8 @@ __metadata: linkType: hard "framer-motion@npm:^10.0.0": - version: 10.16.1 - resolution: "framer-motion@npm:10.16.1" + version: 10.16.4 + resolution: "framer-motion@npm:10.16.4" dependencies: "@emotion/is-prop-valid": ^0.8.2 tslib: ^2.4.0 @@ -6263,7 +6336,7 @@ __metadata: optional: true react-dom: optional: true - checksum: cce24975992020dbbdf426058a2a067ead262f7f264d28d2ed7570cb59e0f175267d7a05d0a1c1335c7bee486bc9dcf1f3940f474e4ba6fc47bb19eebb9f79d2 + checksum: 57eb252f25a2c4ee14b024295c6a1162a53a05e0321bdb9c8a22ec266fbe777832823eaa0309e42854170fcde16c42915c6c5d0208b628fd000d6fab013c501f languageName: node linkType: hard @@ -6694,8 +6767,8 @@ __metadata: linkType: hard "happy-dom@npm:^10.0.0": - version: 10.11.0 - resolution: "happy-dom@npm:10.11.0" + version: 10.11.2 + resolution: "happy-dom@npm:10.11.2" dependencies: css.escape: ^1.5.1 entities: ^4.5.0 @@ -6703,7 +6776,7 @@ __metadata: webidl-conversions: ^7.0.0 whatwg-encoding: ^2.0.0 whatwg-mimetype: ^3.0.0 - checksum: 78231580e3d7aee8dcd809a00adb404a2779236aef04358a7e68e860abafd0503fd8dc5829a411a26328966e9c8a17603f5881e47c949fd1e105786db9e987ba + checksum: 5781985c594f216fc8c6ae1d673aec2ef8c2eaa57b79036775ce42a55537486321bed9e1e3fc994a6a7ee9cc8729097486877f39d796ab9e440a7ba6e82e2401 languageName: node linkType: hard @@ -6875,7 +6948,7 @@ __metadata: "@typescript-eslint/parser": ^6.0.0 "@vitejs/plugin-react": ^4.0.0 "@vitest/coverage-c8": ^0.33.0 - "@vitest/ui": ^0.33.0 + "@vitest/ui": ^0.34.4 axios: ^1.0.0 bcryptjs: ^2.4.3 browser-geo-tz: ^0.0.4 @@ -8750,8 +8823,8 @@ __metadata: linkType: hard "next-i18next@npm:^14.0.0": - version: 14.0.0 - resolution: "next-i18next@npm:14.0.0" + version: 14.0.3 + resolution: "next-i18next@npm:14.0.3" dependencies: "@babel/runtime": ^7.20.13 "@types/hoist-non-react-statics": ^3.3.1 @@ -8759,11 +8832,11 @@ __metadata: hoist-non-react-statics: ^3.3.2 i18next-fs-backend: ^2.1.5 peerDependencies: - i18next: ^23.0.1 + i18next: ^23.4.6 next: ">= 12.0.0" react: ">= 17.0.2" - react-i18next: ^13.0.0 - checksum: a699f6fdee650a2456c463d59910bf38d9f891d224c00de639b15740e8cbf939a2469c8c733007b4f1c2fe0f85c18d43b6f1e814f2127ef30a0d3a28a354dc1d + react-i18next: ^13.2.1 + checksum: 96905e9f800e8536f07d5b6f134e529cc71f02e9a380f4dae4dd07a792be9e943946546f5ba171f6224b6039a3f547cb9f09313ef617339d9d8e68d775da2806 languageName: node linkType: hard @@ -9523,11 +9596,11 @@ __metadata: linkType: hard "prettier@npm:^3.0.0": - version: 3.0.2 - resolution: "prettier@npm:3.0.2" + version: 3.0.3 + resolution: "prettier@npm:3.0.3" bin: prettier: bin/prettier.cjs - checksum: 118b59ddb6c80abe2315ab6d0f4dd1b253be5cfdb20622fa5b65bb1573dcd362e6dd3dcf2711dd3ebfe64aecf7bdc75de8a69dc2422dcd35bdde7610586b677a + checksum: e10b9af02b281f6c617362ebd2571b1d7fc9fb8a3bd17e371754428cda992e5e8d8b7a046e8f7d3e2da1dcd21aa001e2e3c797402ebb6111b5cd19609dd228e0 languageName: node linkType: hard @@ -9570,13 +9643,13 @@ __metadata: linkType: hard "prisma@npm:^5.0.0": - version: 5.2.0 - resolution: "prisma@npm:5.2.0" + version: 5.3.1 + resolution: "prisma@npm:5.3.1" dependencies: - "@prisma/engines": 5.2.0 + "@prisma/engines": 5.3.1 bin: prisma: build/index.js - checksum: 8b99ab5a5f801c72b2eb1809db980bd104dfd699cb14c5d5db5b675566c89e46501267399bb2f02bc3ea8fb86fc2f029cccd7178768109dc4d198bc93552b1da + checksum: e825adbcb4eec81de276de5507fb7e5486db7788c8c9de36ba6ed73f9e87d9f56b64d0e183a31dc6b80f6737ae1fbcdb110aac44ab89299af646aeb966655bef languageName: node linkType: hard @@ -10552,15 +10625,15 @@ __metadata: linkType: hard "sass@npm:^1.56.1": - version: 1.66.1 - resolution: "sass@npm:1.66.1" + version: 1.67.0 + resolution: "sass@npm:1.67.0" 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: 74fc11d0fcd5e16c5331b57dd59865705a299c64e89f2b99646869caeb011dc8d0b6144a6c74a90c264e9ef70654207dbf44fc9b7e3393f8bd14809b904c8a52 + checksum: 9e7566e8b7386cf265dddcdb266a023fb5759c5a8f48a11da199c8bf419e5f08f4ff6404d85d6bf5eac01e1f7c7061fdb6b7b65cbfda164e59b0a06b72ac8567 languageName: node linkType: hard @@ -11608,58 +11681,58 @@ __metadata: languageName: node linkType: hard -"turbo-darwin-64@npm:1.10.13": - version: 1.10.13 - resolution: "turbo-darwin-64@npm:1.10.13" +"turbo-darwin-64@npm:1.10.14": + version: 1.10.14 + resolution: "turbo-darwin-64@npm:1.10.14" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"turbo-darwin-arm64@npm:1.10.13": - version: 1.10.13 - resolution: "turbo-darwin-arm64@npm:1.10.13" +"turbo-darwin-arm64@npm:1.10.14": + version: 1.10.14 + resolution: "turbo-darwin-arm64@npm:1.10.14" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"turbo-linux-64@npm:1.10.13": - version: 1.10.13 - resolution: "turbo-linux-64@npm:1.10.13" +"turbo-linux-64@npm:1.10.14": + version: 1.10.14 + resolution: "turbo-linux-64@npm:1.10.14" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"turbo-linux-arm64@npm:1.10.13": - version: 1.10.13 - resolution: "turbo-linux-arm64@npm:1.10.13" +"turbo-linux-arm64@npm:1.10.14": + version: 1.10.14 + resolution: "turbo-linux-arm64@npm:1.10.14" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"turbo-windows-64@npm:1.10.13": - version: 1.10.13 - resolution: "turbo-windows-64@npm:1.10.13" +"turbo-windows-64@npm:1.10.14": + version: 1.10.14 + resolution: "turbo-windows-64@npm:1.10.14" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"turbo-windows-arm64@npm:1.10.13": - version: 1.10.13 - resolution: "turbo-windows-arm64@npm:1.10.13" +"turbo-windows-arm64@npm:1.10.14": + version: 1.10.14 + resolution: "turbo-windows-arm64@npm:1.10.14" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard "turbo@npm:^1.10.12": - version: 1.10.13 - resolution: "turbo@npm:1.10.13" + version: 1.10.14 + resolution: "turbo@npm:1.10.14" dependencies: - turbo-darwin-64: 1.10.13 - turbo-darwin-arm64: 1.10.13 - turbo-linux-64: 1.10.13 - turbo-linux-arm64: 1.10.13 - turbo-windows-64: 1.10.13 - turbo-windows-arm64: 1.10.13 + turbo-darwin-64: 1.10.14 + turbo-darwin-arm64: 1.10.14 + turbo-linux-64: 1.10.14 + turbo-linux-arm64: 1.10.14 + turbo-windows-64: 1.10.14 + turbo-windows-arm64: 1.10.14 dependenciesMeta: turbo-darwin-64: optional: true @@ -11675,7 +11748,7 @@ __metadata: optional: true bin: turbo: bin/turbo - checksum: 0c000c671534c8c80270c6d1fc77646df0e44164c0db561a85b3fefadd4bda6d5920626d067abb09af38613024e3984fb8d8bc5be922dae6236eda6aab9447a2 + checksum: 219d245bb5cc32a9f76b136b81e86e179228d93a44cab4df3e3d487a55dd2688b5b85f4d585b66568ac53166145352399dd2d7ed0cd47f1aae63d08beb814ebb languageName: node linkType: hard @@ -11788,22 +11861,22 @@ __metadata: linkType: hard "typescript@npm:^5.1.0": - version: 5.1.6 - resolution: "typescript@npm:5.1.6" + version: 5.2.2 + resolution: "typescript@npm:5.2.2" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: b2f2c35096035fe1f5facd1e38922ccb8558996331405eb00a5111cc948b2e733163cc22fab5db46992aba7dd520fff637f2c1df4996ff0e134e77d3249a7350 + checksum: 7912821dac4d962d315c36800fe387cdc0a6298dba7ec171b350b4a6e988b51d7b8f051317786db1094bd7431d526b648aba7da8236607febb26cf5b871d2d3c languageName: node linkType: hard "typescript@patch:typescript@^5.1.0#~builtin": - version: 5.1.6 - resolution: "typescript@patch:typescript@npm%3A5.1.6#~builtin::version=5.1.6&hash=5da071" + version: 5.2.2 + resolution: "typescript@patch:typescript@npm%3A5.2.2#~builtin::version=5.2.2&hash=5da071" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: f53bfe97f7c8b2b6d23cf572750d4e7d1e0c5fff1c36d859d0ec84556a827b8785077bc27676bf7e71fae538e517c3ecc0f37e7f593be913d884805d931bc8be + checksum: 07106822b4305de3f22835cbba949a2b35451cad50888759b6818421290ff95d522b38ef7919e70fb381c5fe9c1c643d7dea22c8b31652a717ddbd57b7f4d554 languageName: node linkType: hard @@ -12046,11 +12119,11 @@ __metadata: linkType: hard "uuid@npm:^9.0.0": - version: 9.0.0 - resolution: "uuid@npm:9.0.0" + version: 9.0.1 + resolution: "uuid@npm:9.0.1" bin: uuid: dist/bin/uuid - checksum: 8dd2c83c43ddc7e1c71e36b60aea40030a6505139af6bee0f382ebcd1a56f6cd3028f7f06ffb07f8cf6ced320b76aea275284b224b002b289f89fe89c389b028 + checksum: 39931f6da74e307f51c0fb463dc2462807531dc80760a9bff1e35af4316131b4fc3203d16da60ae33f07fdca5b56f3f1dd662da0c99fea9aaeab2004780cc5f4 languageName: node linkType: hard @@ -12138,8 +12211,8 @@ __metadata: linkType: hard "vite-tsconfig-paths@npm:^4.2.0": - version: 4.2.0 - resolution: "vite-tsconfig-paths@npm:4.2.0" + version: 4.2.1 + resolution: "vite-tsconfig-paths@npm:4.2.1" dependencies: debug: ^4.1.1 globrex: ^0.1.2 @@ -12149,7 +12222,7 @@ __metadata: peerDependenciesMeta: vite: optional: true - checksum: 73a8467de72d7ac502328454fd00c19571cd4bad2dd5982643b24718bb95e449a3f4153cfc2d58a358bfc8f37e592fb442fc10884b59ae82138c1329160cd952 + checksum: 8cfbd314eb82a9db97e193aef826e72a112ca8b98e68ef0f9cd8f8538fd5163afe67652507bae41d4aab967ab9a8b24dcf95bd0a8900d388cd6c500c5a82d3b1 languageName: node linkType: hard