diff --git a/apps/backend/package.json b/apps/backend/package.json index 04ac76918..c767c3f25 100644 --- a/apps/backend/package.json +++ b/apps/backend/package.json @@ -24,8 +24,8 @@ "@nestjs/schedule": "^4.1.1", "@nestjs/serve-static": "^4.0.2", "pg": "^8.13.0", - "react": "19.0.0-rc-fb9a90fa48-20240614", - "react-dom": "19.0.0-rc-fb9a90fa48-20240614", + "react": "19.0.0-rc-1460d67c-20241003", + "react-dom": "19.0.0-rc-1460d67c-20241003", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1" }, diff --git a/apps/frontend/.gitignore b/apps/frontend/.gitignore index 5bbb99180..26b002aac 100644 --- a/apps/frontend/.gitignore +++ b/apps/frontend/.gitignore @@ -3,7 +3,12 @@ # dependencies /node_modules /.pnp -.pnp.js +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions # testing /coverage @@ -24,12 +29,12 @@ npm-debug.log* yarn-debug.log* yarn-error.log* -# local env files -.env*.local +# env files (can opt-in for commiting if needed) +.env* # vercel .vercel # typescript *.tsbuildinfo -next-env.d.ts \ No newline at end of file +next-env.d.ts diff --git a/apps/frontend/next.config.ts b/apps/frontend/next.config.ts index 0034c61b8..43aec29aa 100644 --- a/apps/frontend/next.config.ts +++ b/apps/frontend/next.config.ts @@ -2,12 +2,6 @@ import type { NextConfig } from 'next'; import VitNodeConfig from 'vitnode-frontend/next.config'; // @ts-ignore -const nextConfig: NextConfig = { - logging: { - fetches: { - fullUrl: process.env.NODE_ENV === 'development', - }, - }, -}; +const nextConfig: NextConfig = {}; export default VitNodeConfig(nextConfig); diff --git a/apps/frontend/package.json b/apps/frontend/package.json index 5a6ad7eed..a090e1cfc 100644 --- a/apps/frontend/package.json +++ b/apps/frontend/package.json @@ -18,10 +18,10 @@ "dependencies": { "geist": "^1.3.1", "lucide-react": "^0.446.0", - "next": "15.0.0-canary.158", - "next-intl": "^3.20.0", - "react": "19.0.0-rc-fb9a90fa48-20240614", - "react-dom": "19.0.0-rc-fb9a90fa48-20240614", + "next": "15.0.0-canary.179", + "next-intl": "3.21.0-canary.0", + "react": "19.0.0-rc-1460d67c-20241003", + "react-dom": "19.0.0-rc-1460d67c-20241003", "react-hook-form": "^7.53.0", "sonner": "^1.5.0", "vitnode-frontend": "workspace:*", diff --git a/apps/frontend/src/app/[locale]/(main)/(vitnode)/[...slug]/page.tsx b/apps/frontend/src/app/[locale]/(main)/(layout)/(vitnode)/[...slug]/page.tsx similarity index 100% rename from apps/frontend/src/app/[locale]/(main)/(vitnode)/[...slug]/page.tsx rename to apps/frontend/src/app/[locale]/(main)/(layout)/(vitnode)/[...slug]/page.tsx diff --git a/apps/frontend/src/app/[locale]/(main)/(vitnode)/legal/[code]/page.tsx b/apps/frontend/src/app/[locale]/(main)/(layout)/(vitnode)/legal/[code]/page.tsx similarity index 100% rename from apps/frontend/src/app/[locale]/(main)/(vitnode)/legal/[code]/page.tsx rename to apps/frontend/src/app/[locale]/(main)/(layout)/(vitnode)/legal/[code]/page.tsx diff --git a/apps/frontend/src/app/[locale]/(main)/(vitnode)/legal/page.tsx b/apps/frontend/src/app/[locale]/(main)/(layout)/(vitnode)/legal/page.tsx similarity index 100% rename from apps/frontend/src/app/[locale]/(main)/(vitnode)/legal/page.tsx rename to apps/frontend/src/app/[locale]/(main)/(layout)/(vitnode)/legal/page.tsx diff --git a/apps/frontend/src/app/[locale]/(main)/(vitnode)/login/layout.tsx b/apps/frontend/src/app/[locale]/(main)/(layout)/(vitnode)/login/layout.tsx similarity index 100% rename from apps/frontend/src/app/[locale]/(main)/(vitnode)/login/layout.tsx rename to apps/frontend/src/app/[locale]/(main)/(layout)/(vitnode)/login/layout.tsx diff --git a/apps/frontend/src/app/[locale]/(main)/(vitnode)/login/page.tsx b/apps/frontend/src/app/[locale]/(main)/(layout)/(vitnode)/login/page.tsx similarity index 100% rename from apps/frontend/src/app/[locale]/(main)/(vitnode)/login/page.tsx rename to apps/frontend/src/app/[locale]/(main)/(layout)/(vitnode)/login/page.tsx diff --git a/apps/frontend/src/app/[locale]/(main)/(layout)/(vitnode)/page.tsx b/apps/frontend/src/app/[locale]/(main)/(layout)/(vitnode)/page.tsx new file mode 100644 index 000000000..5a498984a --- /dev/null +++ b/apps/frontend/src/app/[locale]/(main)/(layout)/(vitnode)/page.tsx @@ -0,0 +1,30 @@ +// ! DO NOT TOUCH THIS FILE!!! IT IS GENERATED BY VITNODE-CLI +// ! If you remove this then default page plugin will not work +import React from 'react'; +import { TranslationsProvider } from 'vitnode-frontend/components/translations-provider'; +import { getSessionData } from 'vitnode-frontend/graphql/get-session-data'; +import { generateMetadataDefaultPage } from 'vitnode-frontend/views/theme/views/default-page'; + +export const generateMetadata = generateMetadataDefaultPage; + +export default async function Page() { + const { + core_sessions__authorization: { plugin_default }, + } = await getSessionData(); + + const PageFromTheme = React.lazy(async () => + import(`@/plugins/${plugin_default}/templates/default-page`).then( + module => ({ + default: module.default, + }), + ), + ); + + return ( + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + + + + ); +} diff --git a/apps/frontend/src/app/[locale]/(main)/(vitnode)/profile/[id]/page.tsx b/apps/frontend/src/app/[locale]/(main)/(layout)/(vitnode)/profile/[id]/page.tsx similarity index 100% rename from apps/frontend/src/app/[locale]/(main)/(vitnode)/profile/[id]/page.tsx rename to apps/frontend/src/app/[locale]/(main)/(layout)/(vitnode)/profile/[id]/page.tsx diff --git a/apps/frontend/src/app/[locale]/(main)/(vitnode)/register/confirm-email/page.tsx b/apps/frontend/src/app/[locale]/(main)/(layout)/(vitnode)/register/confirm-email/page.tsx similarity index 100% rename from apps/frontend/src/app/[locale]/(main)/(vitnode)/register/confirm-email/page.tsx rename to apps/frontend/src/app/[locale]/(main)/(layout)/(vitnode)/register/confirm-email/page.tsx diff --git a/apps/frontend/src/app/[locale]/(main)/(vitnode)/register/layout.tsx b/apps/frontend/src/app/[locale]/(main)/(layout)/(vitnode)/register/layout.tsx similarity index 100% rename from apps/frontend/src/app/[locale]/(main)/(vitnode)/register/layout.tsx rename to apps/frontend/src/app/[locale]/(main)/(layout)/(vitnode)/register/layout.tsx diff --git a/apps/frontend/src/app/[locale]/(main)/(vitnode)/register/page.tsx b/apps/frontend/src/app/[locale]/(main)/(layout)/(vitnode)/register/page.tsx similarity index 100% rename from apps/frontend/src/app/[locale]/(main)/(vitnode)/register/page.tsx rename to apps/frontend/src/app/[locale]/(main)/(layout)/(vitnode)/register/page.tsx diff --git a/apps/frontend/src/app/[locale]/(main)/(vitnode)/settings/devices/page.tsx b/apps/frontend/src/app/[locale]/(main)/(layout)/(vitnode)/settings/devices/page.tsx similarity index 100% rename from apps/frontend/src/app/[locale]/(main)/(vitnode)/settings/devices/page.tsx rename to apps/frontend/src/app/[locale]/(main)/(layout)/(vitnode)/settings/devices/page.tsx diff --git a/apps/frontend/src/app/[locale]/(main)/(vitnode)/settings/files/page.tsx b/apps/frontend/src/app/[locale]/(main)/(layout)/(vitnode)/settings/files/page.tsx similarity index 100% rename from apps/frontend/src/app/[locale]/(main)/(vitnode)/settings/files/page.tsx rename to apps/frontend/src/app/[locale]/(main)/(layout)/(vitnode)/settings/files/page.tsx diff --git a/apps/frontend/src/app/[locale]/(main)/(vitnode)/settings/layout.tsx b/apps/frontend/src/app/[locale]/(main)/(layout)/(vitnode)/settings/layout.tsx similarity index 100% rename from apps/frontend/src/app/[locale]/(main)/(vitnode)/settings/layout.tsx rename to apps/frontend/src/app/[locale]/(main)/(layout)/(vitnode)/settings/layout.tsx diff --git a/apps/frontend/src/app/[locale]/(main)/(vitnode)/settings/page.tsx b/apps/frontend/src/app/[locale]/(main)/(layout)/(vitnode)/settings/page.tsx similarity index 100% rename from apps/frontend/src/app/[locale]/(main)/(vitnode)/settings/page.tsx rename to apps/frontend/src/app/[locale]/(main)/(layout)/(vitnode)/settings/page.tsx diff --git a/apps/frontend/src/app/[locale]/(main)/error.tsx b/apps/frontend/src/app/[locale]/(main)/(layout)/error.tsx similarity index 100% rename from apps/frontend/src/app/[locale]/(main)/error.tsx rename to apps/frontend/src/app/[locale]/(main)/(layout)/error.tsx diff --git a/apps/frontend/src/app/[locale]/(main)/(layout)/layout.tsx b/apps/frontend/src/app/[locale]/(main)/(layout)/layout.tsx new file mode 100644 index 000000000..e2ea5910b --- /dev/null +++ b/apps/frontend/src/app/[locale]/(main)/(layout)/layout.tsx @@ -0,0 +1,9 @@ +import { ThemeLayout } from 'vitnode-frontend/views/theme/layout/theme-layout'; + +export default function LocaleLayout({ + children, +}: { + children: React.ReactNode; +}) { + return {children}; +} diff --git a/apps/frontend/src/app/[locale]/error.tsx b/apps/frontend/src/app/[locale]/(main)/(layout)/not-found.tsx similarity index 50% rename from apps/frontend/src/app/[locale]/error.tsx rename to apps/frontend/src/app/[locale]/(main)/(layout)/not-found.tsx index 1ecb86709..7ee9ca25d 100644 --- a/apps/frontend/src/app/[locale]/error.tsx +++ b/apps/frontend/src/app/[locale]/(main)/(layout)/not-found.tsx @@ -1,7 +1,5 @@ -'use client'; - import { ErrorView } from 'vitnode-frontend/views/theme/views/error/error-view'; -export default function Error() { - return ; +export default function NotFoundPage() { + return ; } diff --git a/apps/frontend/src/app/[locale]/global.css b/apps/frontend/src/app/[locale]/(main)/global.css similarity index 100% rename from apps/frontend/src/app/[locale]/global.css rename to apps/frontend/src/app/[locale]/(main)/global.css diff --git a/apps/frontend/src/app/[locale]/(main)/layout.tsx b/apps/frontend/src/app/[locale]/(main)/layout.tsx index 510cf235c..7524456d0 100644 --- a/apps/frontend/src/app/[locale]/(main)/layout.tsx +++ b/apps/frontend/src/app/[locale]/(main)/layout.tsx @@ -1,11 +1,12 @@ -import React from 'react'; +import { TranslationsProvider } from 'vitnode-frontend/components/translations-provider'; import { AuthLayout } from 'vitnode-frontend/views/layout/auth/auth-layout'; -import { ThemeLayout } from 'vitnode-frontend/views/theme/layout/theme-layout'; + +import './global.css'; export default function Layout({ children }: { children: React.ReactNode }) { return ( - - {children} - + + {children} + ); } diff --git a/apps/frontend/src/app/[locale]/(main)/page.tsx b/apps/frontend/src/app/[locale]/(main)/page.tsx deleted file mode 100644 index 85742ab14..000000000 --- a/apps/frontend/src/app/[locale]/(main)/page.tsx +++ /dev/null @@ -1,21 +0,0 @@ -// ! DO NOT TOUCH THIS FILE!!! IT IS GENERATED BY VITNODE-CLI - -import React from 'react'; -import { - DefaultPage, - DefaultPageProps, - generateMetadataDefaultPage, -} from 'vitnode-frontend/views/theme/views/default-page'; - -export const generateMetadata = generateMetadataDefaultPage; - -export default function Page(props: DefaultPageProps) { - return ( - - import(`@/plugins/${plugin}/templates/default-page`) - } - {...props} - /> - ); -} diff --git a/apps/frontend/src/app/[locale]/admin/(auth)/(vitnode)/core/plugins/[code]/dev/layout.tsx b/apps/frontend/src/app/[locale]/admin/(auth)/(vitnode)/core/plugins/[code]/dev/layout.tsx index bba3c99df..513bdc9cb 100644 --- a/apps/frontend/src/app/[locale]/admin/(auth)/(vitnode)/core/plugins/[code]/dev/layout.tsx +++ b/apps/frontend/src/app/[locale]/admin/(auth)/(vitnode)/core/plugins/[code]/dev/layout.tsx @@ -1,12 +1,13 @@ // ! DO NOT TOUCH THIS FILE!!! IT IS GENERATED BY VITNODE-CLI import { DevPluginAdminLayout, - DevPluginAdminLayoutProps, generateMetadataDevPluginAdminLayout, } from 'vitnode-frontend/admin/core/plugins/views/dev/layout/layout'; export const generateMetadata = generateMetadataDevPluginAdminLayout; -export default function Layout(props: DevPluginAdminLayoutProps) { +export default function Layout( + props: React.ComponentProps, +) { return ; } diff --git a/apps/frontend/src/app/[locale]/admin/layout.tsx b/apps/frontend/src/app/[locale]/admin/layout.tsx index e1f864862..88c2c8682 100644 --- a/apps/frontend/src/app/[locale]/admin/layout.tsx +++ b/apps/frontend/src/app/[locale]/admin/layout.tsx @@ -1,11 +1,9 @@ -// ! DO NOT TOUCH THIS FILE!!! IT IS GENERATED BY VITNODE-CLI import React from 'react'; import 'vitnode-frontend/admin/css'; +import { TranslationsProvider } from 'vitnode-frontend/components/translations-provider'; -export default async function Layout({ - children, -}: { - children: React.ReactNode; -}) { - return children; +export default function Layout({ children }: { children: React.ReactNode }) { + return ( + {children} + ); } diff --git a/apps/frontend/src/app/[locale]/layout.tsx b/apps/frontend/src/app/[locale]/layout.tsx index c3f4f4da6..9303d48e7 100644 --- a/apps/frontend/src/app/[locale]/layout.tsx +++ b/apps/frontend/src/app/[locale]/layout.tsx @@ -2,13 +2,12 @@ import { GeistSans } from 'geist/font/sans'; import { generateMetadataRootLayout, RootLayout, - RootLayoutProps, } from 'vitnode-frontend/views/layout/root-layout'; -import './global.css'; - export const generateMetadata = generateMetadataRootLayout; -export default function Layout(props: RootLayoutProps) { +export default function Layout( + props: Omit, 'className'>, +) { return ; } diff --git a/apps/frontend/src/i18n.ts b/apps/frontend/src/i18n.ts new file mode 100644 index 000000000..499853b67 --- /dev/null +++ b/apps/frontend/src/i18n.ts @@ -0,0 +1,13 @@ +import { getRequestConfig } from 'next-intl/server'; +import { i18nConfigVitNode } from 'vitnode-frontend/i18n'; + +export default getRequestConfig(async ({ requestLocale }) => { + const config = await i18nConfigVitNode({ + pathsToMessagesFromPlugins: async ({ plugin, locale }) => { + return await import(`./plugins/${plugin}/langs/${locale}.json`); + }, + requestLocale, + }); + + return config; +}); diff --git a/apps/frontend/src/i18n/request.ts b/apps/frontend/src/i18n/request.ts deleted file mode 100644 index ffe4b85e0..000000000 --- a/apps/frontend/src/i18n/request.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { getRequestConfig } from 'next-intl/server'; -import { i18nConfig } from 'vitnode-frontend/i18n'; - -export default getRequestConfig(async args => { - const config = await i18nConfig({ - ...args, - pathsToMessagesFromPlugins: async ({ plugin, locale }) => { - return import(`@/plugins/${plugin}/langs/${locale}.json`); - }, - }); - - return config; -}); diff --git a/packages/backend/package.json b/packages/backend/package.json index 924ec8e32..96a9820a6 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -70,7 +70,7 @@ "@react-email/render": "^1.0.1", "@swc/cli": "^0.4.0", "@swc/core": "^1.7.28", - "ai": "^3.4.0", + "ai": "^3.4.9", "busboy": "^1.6.0", "cookie-parser": "^1.4.6", "helmet": "^8.0.0", @@ -109,8 +109,8 @@ "express": "^4.21.0", "graphql": "^16.9.0", "pg": "^8.13.0", - "react": "19.0.0-rc-e56f4ae3-20240830", - "react-dom": "19.0.0-rc-e56f4ae3-20240830", + "react": "19.0.0-rc-1460d67c-20241003", + "react-dom": "19.0.0-rc-1460d67c-20241003", "ts-node": "^10.9.2", "tsup": "^8.3.0", "typescript": "^5.6.2" diff --git a/packages/backend/src/core/admin/members/show/show.service.ts b/packages/backend/src/core/admin/members/show/show.service.ts index 1df14fcbf..e4b858ad6 100644 --- a/packages/backend/src/core/admin/members/show/show.service.ts +++ b/packages/backend/src/core/admin/members/show/show.service.ts @@ -41,11 +41,13 @@ export class ShowAdminMembersService { const where = id ? eq(core_users.id, id) : and( - or( - ilike(core_users.name, `%${search}%`), - ilike(core_users.email, `%${search}%`), - Number(search) ? eq(core_users.id, Number(search)) : undefined, - ), + search + ? or( + ilike(core_users.name, `%${search}%`), + ilike(core_users.email, `%${search}%`), + Number(search) ? eq(core_users.id, Number(search)) : undefined, + ) + : undefined, groups && groups.length > 0 ? inArray(core_users.group_id, groups) : undefined, diff --git a/packages/backend/src/core/sessions/sign_in/sign_in.service.ts b/packages/backend/src/core/sessions/sign_in/sign_in.service.ts index 03a4fa398..31e88e69c 100644 --- a/packages/backend/src/core/sessions/sign_in/sign_in.service.ts +++ b/packages/backend/src/core/sessions/sign_in/sign_in.service.ts @@ -46,13 +46,13 @@ export class SignInCoreSessionsService { const device = await this.deviceService.getDevice({ req, res }); - if (device.uagent_os === 'Uagent from tests') { - throw new CustomError({ - code: 'INVALID_DEVICE', - message: - 'We have detected that you are using an invalid device. Please try again.', - }); - } + // if (device.uagent_os === 'Uagent from tests') { + // throw new CustomError({ + // code: 'INVALID_DEVICE', + // message: + // 'We have detected that you are using an invalid device. Please try again.', + // }); + // } const login_token = this.jwtService.sign( { diff --git a/packages/create-vitnode-app/helpers/create-packages-json.ts b/packages/create-vitnode-app/helpers/create-packages-json.ts index 27602f8b5..757b66901 100644 --- a/packages/create-vitnode-app/helpers/create-packages-json.ts +++ b/packages/create-vitnode-app/helpers/create-packages-json.ts @@ -50,8 +50,8 @@ export const createPackagesJSON = ({ }, overrides: packageManager.startsWith('npm') ? { - react: '^19.0.0-rc.0', - 'react-dom': '^19.0.0-rc.0', + react: '19.0.0-rc-1460d67c-20241003', + 'react-dom': '19.0.0-rc-1460d67c-20241003', } : {}, devDependencies: { @@ -89,10 +89,10 @@ export const createPackagesJSON = ({ '@hookform/resolvers': '^3.9.0', geist: '^1.3.1', 'lucide-react': '^0.446.0', - next: '15.0.0-canary.155', - 'next-intl': '^3.20.0', - react: '^19.0.0-rc-d6cb4e77-20240911', - 'react-dom': '^19.0.0-rc-d6cb4e77-20240911', + next: '15.0.0-canary.179', + 'next-intl': '^3.21.0-canary.0', + react: '19.0.0-rc-1460d67c-20241003', + 'react-dom': '19.0.0-rc-1460d67c-20241003', 'react-hook-form': '^7.53.0', sonner: '^1.5.0', 'vitnode-frontend': `^${pkg.version}`, @@ -141,8 +141,8 @@ export const createPackagesJSON = ({ '@react-email/components': '^0.0.25', 'class-transformer': '^0.5.1', 'class-validator': '^0.14.1', - react: '^19.0.0-rc.0', - 'react-dom': '^19.0.0-rc.0', + react: '19.0.0-rc-1460d67c-20241003', + 'react-dom': '19.0.0-rc-1460d67c-20241003', 'reflect-metadata': '^0.2.2', 'vitnode-backend': `^${pkg.version}`, }, diff --git a/packages/eslint-config-typescript-vitnode/package.json b/packages/eslint-config-typescript-vitnode/package.json index 705fd2867..9e029108b 100644 --- a/packages/eslint-config-typescript-vitnode/package.json +++ b/packages/eslint-config-typescript-vitnode/package.json @@ -37,18 +37,18 @@ }, "devDependencies": { "@types/eslint__js": "^8.42.3", - "eslint": "^9.11.1", + "eslint": "^9.12.0", "typescript": "^5.6.2" }, "dependencies": { - "@eslint/js": "^9.11.1", + "@eslint/js": "^9.12.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-jsx-a11y": "^6.10.0", - "eslint-plugin-perfectionist": "^3.7.0", + "eslint-plugin-perfectionist": "^3.8.0", "eslint-plugin-prettier": "^5.2.1", - "eslint-plugin-react": "^7.37.0", - "globals": "^15.9.0", + "eslint-plugin-react": "^7.37.1", + "globals": "^15.10.0", "prettier-plugin-tailwindcss": "^0.6.8", - "typescript-eslint": "^8.7.0" + "typescript-eslint": "^8.8.0" } } diff --git a/packages/frontend/next.config.mjs b/packages/frontend/next.config.mjs index edd3b75f2..27626acbf 100644 --- a/packages/frontend/next.config.mjs +++ b/packages/frontend/next.config.mjs @@ -6,7 +6,7 @@ dotenv.config({ path: join(process.cwd(), '..', '..', '.env'), }); -const withNextIntl = createNextIntlPlugin('./src/i18n/request.ts'); +const withNextIntl = createNextIntlPlugin('./src/i18n.ts'); const nextConfig = config => { const ENVS = { diff --git a/packages/frontend/package.json b/packages/frontend/package.json index afb280670..05a341ec7 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -89,8 +89,8 @@ "@hookform/resolvers": "^3.9.0", "graphql-tag": "^2.12.6", "lucide-react": "^0.420.0", - "next": "15.0.0-canary.137", - "next-intl": "^3.19.0", + "next": "15.0.0-canary.179", + "next-intl": "^3.21.0-canary.0", "postcss": "^8.4.42", "react": "^19.0.0-rc-d6cb4e77-20240911", "react-dom": "^19.0.0-rc-d6cb4e77-20240911", @@ -108,7 +108,7 @@ "@hookform/resolvers": "^3.9.0", "@swc/cli": "^0.4.0", "@swc/core": "^1.7.28", - "@types/lodash": "^4.17.9", + "@types/lodash": "^4.17.10", "@types/node": "^22.7.4", "@types/nprogress": "^0.2.3", "@types/react": "^18.3.10", @@ -118,10 +118,10 @@ "eslint-config-typescript-vitnode": "workspace:*", "graphql-tag": "^2.12.6", "lucide-react": "^0.446.0", - "next": "15.0.0-canary.158", - "next-intl": "^3.20.0", - "react": "19.0.0-rc-e56f4ae3-20240830", - "react-dom": "19.0.0-rc-e56f4ae3-20240830", + "next": "15.0.0-canary.179", + "next-intl": "3.21.0-canary.0", + "react": "19.0.0-rc-1460d67c-20241003", + "react-dom": "19.0.0-rc-1460d67c-20241003", "react-hook-form": "^7.53.0", "sonner": "^1.5.0", "tailwindcss": "^3.4.13", @@ -134,48 +134,48 @@ "@dnd-kit/sortable": "^8.0.0", "@dnd-kit/utilities": "^3.2.2", "@emoji-mart/data": "^1.2.1", - "@radix-ui/react-accordion": "^1.2.0", - "@radix-ui/react-alert-dialog": "^1.1.1", - "@radix-ui/react-checkbox": "^1.1.1", - "@radix-ui/react-dialog": "^1.1.1", - "@radix-ui/react-dropdown-menu": "^2.1.1", - "@radix-ui/react-hover-card": "^1.1.1", + "@radix-ui/react-accordion": "^1.2.1", + "@radix-ui/react-alert-dialog": "^1.1.2", + "@radix-ui/react-checkbox": "^1.1.2", + "@radix-ui/react-dialog": "^1.1.2", + "@radix-ui/react-dropdown-menu": "^2.1.2", + "@radix-ui/react-hover-card": "^1.1.2", "@radix-ui/react-label": "^2.1.0", - "@radix-ui/react-popover": "^1.1.1", + "@radix-ui/react-popover": "^1.1.2", "@radix-ui/react-progress": "^1.1.0", - "@radix-ui/react-radio-group": "^1.2.0", - "@radix-ui/react-scroll-area": "^1.1.0", - "@radix-ui/react-select": "^2.1.1", + "@radix-ui/react-radio-group": "^1.2.1", + "@radix-ui/react-scroll-area": "^1.2.0", + "@radix-ui/react-select": "^2.1.2", "@radix-ui/react-separator": "^1.1.0", - "@radix-ui/react-slider": "^1.2.0", + "@radix-ui/react-slider": "^1.2.1", "@radix-ui/react-slot": "^1.1.0", - "@radix-ui/react-switch": "^1.1.0", + "@radix-ui/react-switch": "^1.1.1", "@radix-ui/react-toggle": "^1.1.0", "@radix-ui/react-toggle-group": "^1.1.0", - "@radix-ui/react-tooltip": "^1.1.2", + "@radix-ui/react-tooltip": "^1.1.3", "@tailwindcss/container-queries": "^0.1.1", - "@tanstack/react-query": "^5.56.2", - "@tiptap/extension-code-block-lowlight": "^2.7.4", - "@tiptap/extension-color": "^2.7.4", - "@tiptap/extension-heading": "^2.7.4", - "@tiptap/extension-link": "^2.7.4", - "@tiptap/extension-mention": "^2.7.4", - "@tiptap/extension-text-align": "^2.7.4", - "@tiptap/extension-text-style": "^2.7.4", - "@tiptap/extension-underline": "^2.7.4", - "@tiptap/html": "^2.7.4", - "@tiptap/pm": "^2.7.4", - "@tiptap/react": "^2.7.4", - "@tiptap/starter-kit": "^2.7.4", + "@tanstack/react-query": "^5.59.0", + "@tiptap/extension-code-block-lowlight": "^2.8.0", + "@tiptap/extension-color": "^2.8.0", + "@tiptap/extension-heading": "^2.8.0", + "@tiptap/extension-link": "^2.8.0", + "@tiptap/extension-mention": "^2.8.0", + "@tiptap/extension-text-align": "^2.8.0", + "@tiptap/extension-text-style": "^2.8.0", + "@tiptap/extension-underline": "^2.8.0", + "@tiptap/html": "^2.8.0", + "@tiptap/pm": "^2.8.0", + "@tiptap/react": "^2.8.0", + "@tiptap/starter-kit": "^2.8.0", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "cmdk": "^1.0.0", "cropperjs": "^1.6.2", "embla-carousel-react": "^8.3.0", "emoji-mart": "^5.6.0", - "framer-motion": "^11.9.0", + "framer-motion": "^11.11.1", "graphql": "^16.9.0", - "html-react-parser": "^5.1.16", + "html-react-parser": "^5.1.17", "lodash": "^4.17.21", "lowlight": "^3.1.0", "next-themes": "^0.3.0", @@ -184,7 +184,7 @@ "react-cropper": "^2.3.3", "react-day-picker": "^8.10.1", "react-virtuoso": "^4.10.4", - "tailwind-merge": "^2.5.2", + "tailwind-merge": "^2.5.3", "tailwindcss-animate": "^1.0.7", "tippy.js": "^6.3.7", "use-debounce": "^10.0.3", diff --git a/packages/frontend/src/graphql/fetcher.ts b/packages/frontend/src/graphql/fetcher.ts index 4bd81114f..d4f537b7a 100644 --- a/packages/frontend/src/graphql/fetcher.ts +++ b/packages/frontend/src/graphql/fetcher.ts @@ -38,13 +38,13 @@ const cookieFromStringToObject = ( export const setCookieFromApi = ({ res }: { res: Response }) => { return cookieFromStringToObject(res.headers.getSetCookie()).forEach( - cookie => { + async cookie => { const key = Object.keys(cookie)[0]; const value = Object.values(cookie)[0]; if (typeof value !== 'string' || typeof key !== 'string') return; - cookies().set(key, value, { + (await cookies()).set(key, value, { domain: cookie.Domain, path: cookie.Path, expires: new Date(cookie.Expires), @@ -150,15 +150,18 @@ export async function fetcher({ }); } - const nextInternalHeaders = nextHeaders(); + const [nextInternalHeaders, cookie] = await Promise.all([ + nextHeaders(), + cookies(), + ]); const internalHeaders = { - Cookie: cookies().toString(), + Cookie: cookie.toString(), ['user-agent']: nextInternalHeaders.get('user-agent') ?? 'node', ['x-forwarded-for']: nextInternalHeaders.get('x-forwarded-for') ?? '0.0.0.0', ['x-real-ip']: nextInternalHeaders.get('x-real-ip') ?? '0.0.0.0', - 'x-vitnode-user-language': cookies().get('NEXT_LOCALE')?.value ?? 'en', + 'x-vitnode-user-language': cookie.get('NEXT_LOCALE')?.value ?? 'en', ...headers, }; diff --git a/packages/frontend/src/graphql/get-pagination-tool.ts b/packages/frontend/src/graphql/get-pagination-tool.ts index f239fa66b..262335219 100644 --- a/packages/frontend/src/graphql/get-pagination-tool.ts +++ b/packages/frontend/src/graphql/get-pagination-tool.ts @@ -11,7 +11,7 @@ export interface SearchParamsPagination { interface Args { defaultPageSize: 10 | 20 | 30 | 40 | 50; - searchParams: SearchParamsPagination; + searchParams: Promise; search?: boolean; sortByEnum?: T; } @@ -23,11 +23,13 @@ interface ReturnValues { sortBy?: { column: keyof T; direction: SortDirectionEnum }; } -export function getPaginationTool>({ +export async function getPaginationTool>({ defaultPageSize, - searchParams, + searchParams: searchParamsPromise, sortByEnum, -}: Args): ReturnValues { +}: Args): Promise> { + const searchParams = await searchParamsPromise; + const pagination = { first: Number(searchParams?.last ?? 0) ? null diff --git a/packages/frontend/src/graphql/get-user-id-cookie.ts b/packages/frontend/src/graphql/get-user-id-cookie.ts index d9166ee24..1b3650564 100644 --- a/packages/frontend/src/graphql/get-user-id-cookie.ts +++ b/packages/frontend/src/graphql/get-user-id-cookie.ts @@ -1,10 +1,10 @@ import { cookies } from 'next/headers'; import 'server-only'; -export const getUserIdCookie = () => { - return cookies().get('vitnode-user-id')?.value; +export const getUserIdCookie = async () => { + return (await cookies()).get('vitnode-user-id')?.value; }; -export const getAdminIdCookie = () => { - return cookies().get('vitnode-admin-id')?.value; +export const getAdminIdCookie = async () => { + return (await cookies()).get('vitnode-admin-id')?.value; }; diff --git a/packages/frontend/src/hooks/core/sign/in/mutation-api.ts b/packages/frontend/src/hooks/core/sign/in/mutation-api.ts index da07e1f53..976cc1737 100644 --- a/packages/frontend/src/hooks/core/sign/in/mutation-api.ts +++ b/packages/frontend/src/hooks/core/sign/in/mutation-api.ts @@ -10,6 +10,7 @@ import { import { revalidateTags } from '@/graphql/revalidate-tags'; import { redirect } from '@/navigation'; import { cookies } from 'next/headers'; +import { getLocale } from 'next-intl/server'; export const mutationApi = async ( variables: Core_Sessions__Sign_InMutationVariables, @@ -23,14 +24,14 @@ export const mutationApi = async ( variables, }); - const cookie = cookies(); + const cookie = await cookies(); if (!variables.admin) { const userIdFromCookie = cookie.get('vitnode-user-id')?.value; if (userIdFromCookie) { revalidateTags.session(+userIdFromCookie); } } else { - const adminIdFromCookie = getAdminIdCookie(); + const adminIdFromCookie = await getAdminIdCookie(); if (adminIdFromCookie) { revalidateTags.sessionAdmin(+adminIdFromCookie); } @@ -41,5 +42,8 @@ export const mutationApi = async ( return { error: e.message }; } - redirect(variables.admin ? '/admin/core/dashboard' : '/'); + if (variables.admin) { + const locale = await getLocale(); + redirect({ href: '/admin/core/dashboard', locale }); + } }; diff --git a/packages/frontend/src/hooks/core/sign/in/use-sign-in-view.ts b/packages/frontend/src/hooks/core/sign/in/use-sign-in-view.ts index c9d01e8f5..009e6ead6 100644 --- a/packages/frontend/src/hooks/core/sign/in/use-sign-in-view.ts +++ b/packages/frontend/src/hooks/core/sign/in/use-sign-in-view.ts @@ -1,4 +1,5 @@ import React from 'react'; +import { UseFormReturn } from 'react-hook-form'; import * as z from 'zod'; import { mutationApi } from './mutation-api'; @@ -12,12 +13,19 @@ export const useSignInView = () => { remember: z.boolean().default(false).optional(), }); - const onSubmit = async (values: z.infer) => { + const onSubmit = async ( + values: z.infer, + form: UseFormReturn>, + ) => { setError(''); + form.reset({}, { keepValues: true }); const mutation = await mutationApi(values); if (mutation?.error) { setError(mutation.error); } + + // Redirect to home + window.location.href = '/'; }; return { diff --git a/packages/frontend/src/hooks/core/sign/out/mutation-api.ts b/packages/frontend/src/hooks/core/sign/out/mutation-api.ts index ae80dea88..2b6611b62 100644 --- a/packages/frontend/src/hooks/core/sign/out/mutation-api.ts +++ b/packages/frontend/src/hooks/core/sign/out/mutation-api.ts @@ -8,7 +8,6 @@ import { Core_Sessions__Sign_OutMutationVariables, } from '@/graphql/mutations/sessions/core_sessions__sign_out.generated'; import { revalidateTags } from '@/graphql/revalidate-tags'; -import { redirect } from '@/navigation'; export const mutationApi = async () => { try { @@ -19,7 +18,7 @@ export const mutationApi = async () => { query: Core_Sessions__Sign_Out, }); - const userIdFromCookie = getUserIdCookie(); + const userIdFromCookie = await getUserIdCookie(); if (userIdFromCookie) { revalidateTags.session(+userIdFromCookie); } @@ -28,6 +27,4 @@ export const mutationApi = async () => { return { error: e.message }; } - - redirect('/'); }; diff --git a/packages/frontend/src/hooks/core/sign/out/use-sign-out-api.ts b/packages/frontend/src/hooks/core/sign/out/use-sign-out-api.ts index aac133a18..bc86e6255 100644 --- a/packages/frontend/src/hooks/core/sign/out/use-sign-out-api.ts +++ b/packages/frontend/src/hooks/core/sign/out/use-sign-out-api.ts @@ -14,6 +14,9 @@ export const useSignOutApi = () => { description: t('internal_server_error'), }); } + + // Redirect to home + window.location.href = '/'; }; return { diff --git a/packages/frontend/src/i18n.ts b/packages/frontend/src/i18n.ts index 6685ba24a..631bce78a 100644 --- a/packages/frontend/src/i18n.ts +++ b/packages/frontend/src/i18n.ts @@ -1,26 +1,30 @@ -import { notFound } from 'next/navigation'; -import { IntlConfig } from 'next-intl'; - -import { fetcher } from './graphql/fetcher'; +import { fetcher } from 'vitnode-frontend/graphql/fetcher'; import { Core_Middleware__Show, Core_Middleware__ShowQuery, Core_Middleware__ShowQueryVariables, -} from './graphql/queries/core_middleware__show.generated'; +} from 'vitnode-frontend/graphql/queries/core_middleware__show.generated'; -export const i18nConfig = async ({ +export const i18nConfigVitNode = async ({ pathsToMessagesFromPlugins, - locale, + requestLocale, }: { - locale: string; pathsToMessagesFromPlugins: ({ plugin, locale, }: { locale: string; plugin: string; - }) => Promise<{ default: unknown }>; -}): Promise> => { + }) => Promise<{ default: object }>; + requestLocale: Promise; +}) => { + let locale = await requestLocale; + let defaultLocale = 'en'; + + if (!locale) { + locale = 'en'; + } + let plugins: string[] = []; try { const { @@ -31,39 +35,33 @@ export const i18nConfig = async ({ >({ query: Core_Middleware__Show, }); + plugins = pluginsFromServer; + const defaultLanguage = languages.find(lang => lang.default); + defaultLocale = defaultLanguage?.code ?? 'en'; if (!languages.find(lang => lang.code === locale)) { - notFound(); + locale = defaultLanguage?.code; } - - plugins = pluginsFromServer; } catch (_) { - plugins = ['core', 'admin']; + // If the request fails, we will use the default plugins + plugins = ['core', 'admin', 'welcome']; } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const messagesFormApps: any[] = await Promise.all( - plugins.map(async plugin => { - try { - const message = await pathsToMessagesFromPlugins({ - plugin, - locale, - }); + let messages = {}; + for (const plugin of plugins) { + const message = ( + await pathsToMessagesFromPlugins({ + plugin, + locale: locale ?? defaultLocale, + }) + ).default; - return message.default; - } catch (_) { - return {}; - } - }), - ); + messages = { ...messages, ...message }; + } return { - messages: { - ...messagesFormApps.reduce( - (acc, messages) => ({ ...acc, ...messages }), - {}, - ), - }, + locale, + messages, timeZone: 'UTC', }; }; diff --git a/packages/frontend/src/middleware.ts b/packages/frontend/src/middleware.ts index b00951f72..4f0488dbe 100644 --- a/packages/frontend/src/middleware.ts +++ b/packages/frontend/src/middleware.ts @@ -62,7 +62,6 @@ export function createMiddleware() { ...i18n, localePrefix: i18n.locales.length > 1 ? 'always' : 'as-needed', }); - const response = handleI18nRouting(request); const pathname = removeLocaleFromUrl( request.nextUrl.pathname, i18n.locales, @@ -97,6 +96,6 @@ export function createMiddleware() { return NextResponse.redirect(new URL('/admin', request.url)); } - return response; + return handleI18nRouting(request); }; } diff --git a/packages/frontend/src/navigation/index.ts b/packages/frontend/src/navigation/index.ts index b3621ca2c..6245dfbd8 100644 --- a/packages/frontend/src/navigation/index.ts +++ b/packages/frontend/src/navigation/index.ts @@ -1,7 +1,7 @@ -import { createSharedPathnamesNavigation } from 'next-intl/navigation'; +import { createNavigation } from 'next-intl/navigation'; import { useRouter } from './router'; -const { redirect, usePathname, Link } = createSharedPathnamesNavigation(); +const { redirect, usePathname, Link } = createNavigation(); export { Link, redirect, usePathname, useRouter }; diff --git a/packages/frontend/src/navigation/router.ts b/packages/frontend/src/navigation/router.ts index 18c2b385f..3cd7a5a53 100644 --- a/packages/frontend/src/navigation/router.ts +++ b/packages/frontend/src/navigation/router.ts @@ -1,12 +1,11 @@ 'use client'; import { useSearchParams } from 'next/navigation'; -import { createSharedPathnamesNavigation } from 'next-intl/navigation'; +import { createNavigation } from 'next-intl/navigation'; import NProgress from 'nprogress'; import React from 'react'; -const { useRouter: useRouterI18n, usePathname } = - createSharedPathnamesNavigation(); +const { useRouter: useRouterI18n, usePathname } = createNavigation(); const useRouter = () => { const pathname = usePathname(); diff --git a/packages/frontend/src/views/admin/layout/admin-layout.tsx b/packages/frontend/src/views/admin/layout/admin-layout.tsx index 055064955..917f4d7fc 100644 --- a/packages/frontend/src/views/admin/layout/admin-layout.tsx +++ b/packages/frontend/src/views/admin/layout/admin-layout.tsx @@ -30,9 +30,12 @@ export const generateMetadataAdminLayout = async (): Promise => { export const AdminLayout = async ({ children, + params, }: { children: React.ReactNode; + params: Promise<{ locale: string }>; }) => { + const { locale } = await params; try { const [t, data] = await Promise.all([ getTranslations(), @@ -109,7 +112,7 @@ export const AdminLayout = async ({ ); } catch (err) { if (err instanceof Error && err.message === 'ACCESS_DENIED') { - redirect('/admin'); + redirect({ href: '/admin', locale }); } throw err; diff --git a/packages/frontend/src/views/admin/layout/auth/aside/aside.tsx b/packages/frontend/src/views/admin/layout/auth/aside/aside.tsx index 2754de059..8547aeca8 100644 --- a/packages/frontend/src/views/admin/layout/auth/aside/aside.tsx +++ b/packages/frontend/src/views/admin/layout/auth/aside/aside.tsx @@ -15,7 +15,7 @@ export const AsideAuthAdmin = ({ textsAndIcons: TextAndIconsAsideAdmin[]; }) => { return ( -