From 3261f2b582bbfb3aad34f7c67d1c3ed8dee85e78 Mon Sep 17 00:00:00 2001 From: giancarlo Date: Tue, 4 Jun 2024 11:54:04 +0700 Subject: [PATCH] Add more efficient authentication check function to server components. Added request logging to Next.js config. This commit introduces a new function 'requireUserInServerComponent' which checks for user authentication and is used in multiple server components. The aim is to enhance efficiency by caching the function so that data is only fetched once per request, preventing unnecessary database hits. Existing components were modified accordingly to incorporate this new method. --- .../(user)/_lib/server/load-user-workspace.ts | 14 +++-------- apps/web/app/home/(user)/billing/page.tsx | 16 +++--------- .../server/team-account-workspace.loader.ts | 12 +++------ .../home/[account]/billing/return/page.tsx | 9 +++---- apps/web/app/update-password/page.tsx | 13 ++-------- .../require-user-in-server-component.ts | 25 +++++++++++++++++++ apps/web/next.config.mjs | 5 ++++ 7 files changed, 44 insertions(+), 50 deletions(-) create mode 100644 apps/web/lib/server/require-user-in-server-component.ts diff --git a/apps/web/app/home/(user)/_lib/server/load-user-workspace.ts b/apps/web/app/home/(user)/_lib/server/load-user-workspace.ts index 8ddca670c..f9d0ea0ea 100644 --- a/apps/web/app/home/(user)/_lib/server/load-user-workspace.ts +++ b/apps/web/app/home/(user)/_lib/server/load-user-workspace.ts @@ -1,12 +1,10 @@ import { cache } from 'react'; -import { redirect } from 'next/navigation'; - import { createAccountsApi } from '@kit/accounts/api'; -import { requireUser } from '@kit/supabase/require-user'; import { getSupabaseServerComponentClient } from '@kit/supabase/server-component-client'; import featureFlagsConfig from '~/config/feature-flags.config'; +import { requireUserInServerComponent } from '~/lib/server/require-user-in-server-component'; const shouldLoadAccounts = featureFlagsConfig.enableTeamAccounts; @@ -30,18 +28,12 @@ async function workspaceLoader() { const workspacePromise = api.getAccountWorkspace(); - const [accounts, workspace, auth] = await Promise.all([ + const [accounts, workspace, user] = await Promise.all([ accountsPromise(), workspacePromise, - requireUser(client), + requireUserInServerComponent(), ]); - if (!auth.data) { - return redirect(auth.redirectTo); - } - - const user = auth.data; - return { accounts, workspace, diff --git a/apps/web/app/home/(user)/billing/page.tsx b/apps/web/app/home/(user)/billing/page.tsx index c26dc03a1..6ea32ef5f 100644 --- a/apps/web/app/home/(user)/billing/page.tsx +++ b/apps/web/app/home/(user)/billing/page.tsx @@ -1,12 +1,8 @@ -import { redirect } from 'next/navigation'; - import { BillingPortalCard, CurrentLifetimeOrderCard, CurrentSubscriptionCard, } from '@kit/billing-gateway/components'; -import { requireUser } from '@kit/supabase/require-user'; -import { getSupabaseServerComponentClient } from '@kit/supabase/server-component-client'; import { If } from '@kit/ui/if'; import { PageBody } from '@kit/ui/page'; import { Trans } from '@kit/ui/trans'; @@ -14,6 +10,7 @@ import { Trans } from '@kit/ui/trans'; import billingConfig from '~/config/billing.config'; import { createI18nServerInstance } from '~/lib/i18n/i18n.server'; import { withI18n } from '~/lib/i18n/with-i18n'; +import { requireUserInServerComponent } from '~/lib/server/require-user-in-server-component'; // local imports import { HomeLayoutPageHeader } from '../_components/home-page-header'; @@ -31,16 +28,9 @@ export const generateMetadata = async () => { }; async function PersonalAccountBillingPage() { - const client = getSupabaseServerComponentClient(); - const auth = await requireUser(client); + const user = await requireUserInServerComponent(); - if (auth.error) { - redirect(auth.redirectTo); - } - - const [data, customerId] = await loadPersonalAccountBillingPageData( - auth.data.id, - ); + const [data, customerId] = await loadPersonalAccountBillingPageData(user.id); return ( <> diff --git a/apps/web/app/home/[account]/_lib/server/team-account-workspace.loader.ts b/apps/web/app/home/[account]/_lib/server/team-account-workspace.loader.ts index 9154fc87e..40a204f4d 100644 --- a/apps/web/app/home/[account]/_lib/server/team-account-workspace.loader.ts +++ b/apps/web/app/home/[account]/_lib/server/team-account-workspace.loader.ts @@ -4,11 +4,11 @@ import { cache } from 'react'; import { redirect } from 'next/navigation'; -import { requireUser } from '@kit/supabase/require-user'; import { getSupabaseServerComponentClient } from '@kit/supabase/server-component-client'; import { createTeamAccountsApi } from '@kit/team-accounts/api'; import pathsConfig from '~/config/paths.config'; +import { requireUserInServerComponent } from '~/lib/server/require-user-in-server-component'; export type TeamAccountWorkspace = Awaited< ReturnType @@ -29,9 +29,9 @@ async function workspaceLoader(accountSlug: string) { const client = getSupabaseServerComponentClient(); const api = createTeamAccountsApi(client); - const [workspace, auth] = await Promise.all([ + const [workspace, user] = await Promise.all([ api.getAccountWorkspace(accountSlug), - requireUser(client), + requireUserInServerComponent(), ]); // we cannot find any record for the selected account @@ -40,12 +40,6 @@ async function workspaceLoader(accountSlug: string) { return redirect(pathsConfig.app.home); } - if (!auth.data) { - return redirect(auth.redirectTo); - } - - const user = auth.data; - return { ...workspace.data, user, diff --git a/apps/web/app/home/[account]/billing/return/page.tsx b/apps/web/app/home/[account]/billing/return/page.tsx index 5081450cf..072260f54 100644 --- a/apps/web/app/home/[account]/billing/return/page.tsx +++ b/apps/web/app/home/[account]/billing/return/page.tsx @@ -9,6 +9,7 @@ import { getSupabaseServerComponentClient } from '@kit/supabase/server-component import billingConfig from '~/config/billing.config'; import { withI18n } from '~/lib/i18n/with-i18n'; +import { requireUserInServerComponent } from '~/lib/server/require-user-in-server-component'; interface SessionPageProps { searchParams: { @@ -73,13 +74,9 @@ function BlurryBackdrop() { } async function loadCheckoutSession(sessionId: string) { + await requireUserInServerComponent(); + const client = getSupabaseServerComponentClient(); - const { error } = await requireUser(client); - - if (error) { - throw new Error('Authentication required'); - } - const gateway = await getBillingGatewayProvider(client); const session = await gateway.retrieveCheckoutSession({ diff --git a/apps/web/app/update-password/page.tsx b/apps/web/app/update-password/page.tsx index a8fffd936..fea8e33d5 100644 --- a/apps/web/app/update-password/page.tsx +++ b/apps/web/app/update-password/page.tsx @@ -1,14 +1,11 @@ -import { redirect } from 'next/navigation'; - import { UpdatePasswordForm } from '@kit/auth/password-reset'; import { AuthLayoutShell } from '@kit/auth/shared'; -import { requireUser } from '@kit/supabase/require-user'; -import { getSupabaseServerComponentClient } from '@kit/supabase/server-component-client'; import { AppLogo } from '~/components/app-logo'; import pathsConfig from '~/config/paths.config'; import { createI18nServerInstance } from '~/lib/i18n/i18n.server'; import { withI18n } from '~/lib/i18n/with-i18n'; +import { requireUserInServerComponent } from '~/lib/server/require-user-in-server-component'; export const generateMetadata = async () => { const { t } = await createI18nServerInstance(); @@ -19,13 +16,7 @@ export const generateMetadata = async () => { }; async function UpdatePasswordPage() { - const client = getSupabaseServerComponentClient(); - const auth = await requireUser(client); - - // we require the user to be logged in to access this page - if (auth.error) { - redirect(auth.redirectTo); - } + await requireUserInServerComponent(); return ( diff --git a/apps/web/lib/server/require-user-in-server-component.ts b/apps/web/lib/server/require-user-in-server-component.ts new file mode 100644 index 000000000..6e87274f7 --- /dev/null +++ b/apps/web/lib/server/require-user-in-server-component.ts @@ -0,0 +1,25 @@ +import 'server-only'; + +import { cache } from 'react'; + +import { redirect } from 'next/navigation'; + +import { requireUser } from '@kit/supabase/require-user'; +import { getSupabaseServerComponentClient } from '@kit/supabase/server-component-client'; + +/** + * @name requireUserInServerComponent + * @description Require the user to be authenticated in a server component. + * We reuse this function in multiple server components - it is cached so that the data is only fetched once per request. + * Use this instead of `requireUser` in server components, so you don't need to hit the database multiple times in a single request. + */ +export const requireUserInServerComponent = cache(async () => { + const client = getSupabaseServerComponentClient(); + const result = await requireUser(client); + + if (result.error) { + redirect(result.redirectTo); + } + + return result.data; +}); diff --git a/apps/web/next.config.mjs b/apps/web/next.config.mjs index 4e376998a..e9cc3dd91 100644 --- a/apps/web/next.config.mjs +++ b/apps/web/next.config.mjs @@ -30,6 +30,11 @@ const config = { images: { remotePatterns: getRemotePatterns(), }, + logging: { + fetches: { + fullUrl: true, + }, + }, experimental: { mdxRs: true, instrumentationHook: true,