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.
This commit is contained in:
@@ -1,12 +1,10 @@
|
|||||||
import { cache } from 'react';
|
import { cache } from 'react';
|
||||||
|
|
||||||
import { redirect } from 'next/navigation';
|
|
||||||
|
|
||||||
import { createAccountsApi } from '@kit/accounts/api';
|
import { createAccountsApi } from '@kit/accounts/api';
|
||||||
import { requireUser } from '@kit/supabase/require-user';
|
|
||||||
import { getSupabaseServerComponentClient } from '@kit/supabase/server-component-client';
|
import { getSupabaseServerComponentClient } from '@kit/supabase/server-component-client';
|
||||||
|
|
||||||
import featureFlagsConfig from '~/config/feature-flags.config';
|
import featureFlagsConfig from '~/config/feature-flags.config';
|
||||||
|
import { requireUserInServerComponent } from '~/lib/server/require-user-in-server-component';
|
||||||
|
|
||||||
const shouldLoadAccounts = featureFlagsConfig.enableTeamAccounts;
|
const shouldLoadAccounts = featureFlagsConfig.enableTeamAccounts;
|
||||||
|
|
||||||
@@ -30,18 +28,12 @@ async function workspaceLoader() {
|
|||||||
|
|
||||||
const workspacePromise = api.getAccountWorkspace();
|
const workspacePromise = api.getAccountWorkspace();
|
||||||
|
|
||||||
const [accounts, workspace, auth] = await Promise.all([
|
const [accounts, workspace, user] = await Promise.all([
|
||||||
accountsPromise(),
|
accountsPromise(),
|
||||||
workspacePromise,
|
workspacePromise,
|
||||||
requireUser(client),
|
requireUserInServerComponent(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (!auth.data) {
|
|
||||||
return redirect(auth.redirectTo);
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = auth.data;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
accounts,
|
accounts,
|
||||||
workspace,
|
workspace,
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
import { redirect } from 'next/navigation';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
BillingPortalCard,
|
BillingPortalCard,
|
||||||
CurrentLifetimeOrderCard,
|
CurrentLifetimeOrderCard,
|
||||||
CurrentSubscriptionCard,
|
CurrentSubscriptionCard,
|
||||||
} from '@kit/billing-gateway/components';
|
} 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 { If } from '@kit/ui/if';
|
||||||
import { PageBody } from '@kit/ui/page';
|
import { PageBody } from '@kit/ui/page';
|
||||||
import { Trans } from '@kit/ui/trans';
|
import { Trans } from '@kit/ui/trans';
|
||||||
@@ -14,6 +10,7 @@ import { Trans } from '@kit/ui/trans';
|
|||||||
import billingConfig from '~/config/billing.config';
|
import billingConfig from '~/config/billing.config';
|
||||||
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
|
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
|
||||||
import { withI18n } from '~/lib/i18n/with-i18n';
|
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||||
|
import { requireUserInServerComponent } from '~/lib/server/require-user-in-server-component';
|
||||||
|
|
||||||
// local imports
|
// local imports
|
||||||
import { HomeLayoutPageHeader } from '../_components/home-page-header';
|
import { HomeLayoutPageHeader } from '../_components/home-page-header';
|
||||||
@@ -31,16 +28,9 @@ export const generateMetadata = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
async function PersonalAccountBillingPage() {
|
async function PersonalAccountBillingPage() {
|
||||||
const client = getSupabaseServerComponentClient();
|
const user = await requireUserInServerComponent();
|
||||||
const auth = await requireUser(client);
|
|
||||||
|
|
||||||
if (auth.error) {
|
const [data, customerId] = await loadPersonalAccountBillingPageData(user.id);
|
||||||
redirect(auth.redirectTo);
|
|
||||||
}
|
|
||||||
|
|
||||||
const [data, customerId] = await loadPersonalAccountBillingPageData(
|
|
||||||
auth.data.id,
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ import { cache } from 'react';
|
|||||||
|
|
||||||
import { redirect } from 'next/navigation';
|
import { redirect } from 'next/navigation';
|
||||||
|
|
||||||
import { requireUser } from '@kit/supabase/require-user';
|
|
||||||
import { getSupabaseServerComponentClient } from '@kit/supabase/server-component-client';
|
import { getSupabaseServerComponentClient } from '@kit/supabase/server-component-client';
|
||||||
import { createTeamAccountsApi } from '@kit/team-accounts/api';
|
import { createTeamAccountsApi } from '@kit/team-accounts/api';
|
||||||
|
|
||||||
import pathsConfig from '~/config/paths.config';
|
import pathsConfig from '~/config/paths.config';
|
||||||
|
import { requireUserInServerComponent } from '~/lib/server/require-user-in-server-component';
|
||||||
|
|
||||||
export type TeamAccountWorkspace = Awaited<
|
export type TeamAccountWorkspace = Awaited<
|
||||||
ReturnType<typeof loadTeamWorkspace>
|
ReturnType<typeof loadTeamWorkspace>
|
||||||
@@ -29,9 +29,9 @@ async function workspaceLoader(accountSlug: string) {
|
|||||||
const client = getSupabaseServerComponentClient();
|
const client = getSupabaseServerComponentClient();
|
||||||
const api = createTeamAccountsApi(client);
|
const api = createTeamAccountsApi(client);
|
||||||
|
|
||||||
const [workspace, auth] = await Promise.all([
|
const [workspace, user] = await Promise.all([
|
||||||
api.getAccountWorkspace(accountSlug),
|
api.getAccountWorkspace(accountSlug),
|
||||||
requireUser(client),
|
requireUserInServerComponent(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// we cannot find any record for the selected account
|
// we cannot find any record for the selected account
|
||||||
@@ -40,12 +40,6 @@ async function workspaceLoader(accountSlug: string) {
|
|||||||
return redirect(pathsConfig.app.home);
|
return redirect(pathsConfig.app.home);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!auth.data) {
|
|
||||||
return redirect(auth.redirectTo);
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = auth.data;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...workspace.data,
|
...workspace.data,
|
||||||
user,
|
user,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { getSupabaseServerComponentClient } from '@kit/supabase/server-component
|
|||||||
|
|
||||||
import billingConfig from '~/config/billing.config';
|
import billingConfig from '~/config/billing.config';
|
||||||
import { withI18n } from '~/lib/i18n/with-i18n';
|
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||||
|
import { requireUserInServerComponent } from '~/lib/server/require-user-in-server-component';
|
||||||
|
|
||||||
interface SessionPageProps {
|
interface SessionPageProps {
|
||||||
searchParams: {
|
searchParams: {
|
||||||
@@ -73,13 +74,9 @@ function BlurryBackdrop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function loadCheckoutSession(sessionId: string) {
|
async function loadCheckoutSession(sessionId: string) {
|
||||||
|
await requireUserInServerComponent();
|
||||||
|
|
||||||
const client = getSupabaseServerComponentClient();
|
const client = getSupabaseServerComponentClient();
|
||||||
const { error } = await requireUser(client);
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
throw new Error('Authentication required');
|
|
||||||
}
|
|
||||||
|
|
||||||
const gateway = await getBillingGatewayProvider(client);
|
const gateway = await getBillingGatewayProvider(client);
|
||||||
|
|
||||||
const session = await gateway.retrieveCheckoutSession({
|
const session = await gateway.retrieveCheckoutSession({
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
import { redirect } from 'next/navigation';
|
|
||||||
|
|
||||||
import { UpdatePasswordForm } from '@kit/auth/password-reset';
|
import { UpdatePasswordForm } from '@kit/auth/password-reset';
|
||||||
import { AuthLayoutShell } from '@kit/auth/shared';
|
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 { AppLogo } from '~/components/app-logo';
|
||||||
import pathsConfig from '~/config/paths.config';
|
import pathsConfig from '~/config/paths.config';
|
||||||
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
|
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
|
||||||
import { withI18n } from '~/lib/i18n/with-i18n';
|
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||||
|
import { requireUserInServerComponent } from '~/lib/server/require-user-in-server-component';
|
||||||
|
|
||||||
export const generateMetadata = async () => {
|
export const generateMetadata = async () => {
|
||||||
const { t } = await createI18nServerInstance();
|
const { t } = await createI18nServerInstance();
|
||||||
@@ -19,13 +16,7 @@ export const generateMetadata = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
async function UpdatePasswordPage() {
|
async function UpdatePasswordPage() {
|
||||||
const client = getSupabaseServerComponentClient();
|
await requireUserInServerComponent();
|
||||||
const auth = await requireUser(client);
|
|
||||||
|
|
||||||
// we require the user to be logged in to access this page
|
|
||||||
if (auth.error) {
|
|
||||||
redirect(auth.redirectTo);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AuthLayoutShell Logo={AppLogo}>
|
<AuthLayoutShell Logo={AppLogo}>
|
||||||
|
|||||||
25
apps/web/lib/server/require-user-in-server-component.ts
Normal file
25
apps/web/lib/server/require-user-in-server-component.ts
Normal file
@@ -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;
|
||||||
|
});
|
||||||
@@ -30,6 +30,11 @@ const config = {
|
|||||||
images: {
|
images: {
|
||||||
remotePatterns: getRemotePatterns(),
|
remotePatterns: getRemotePatterns(),
|
||||||
},
|
},
|
||||||
|
logging: {
|
||||||
|
fetches: {
|
||||||
|
fullUrl: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
experimental: {
|
experimental: {
|
||||||
mdxRs: true,
|
mdxRs: true,
|
||||||
instrumentationHook: true,
|
instrumentationHook: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user