Skip to content

Commit

Permalink
feat: vip shortcut implemented 'properly', preserving the SSG
Browse files Browse the repository at this point in the history
  • Loading branch information
gustaveWPM committed Apr 23, 2024
1 parent dc462ad commit 2e5c8d7
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 18 deletions.
4 changes: 4 additions & 0 deletions interop/config/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,9 @@

export const APP_PROTECTED_PATHS = ['/dashboard'];

export const VIP_SHORTCUTS = {
'/lp/sign-up': '/dashboard'
} as const;

// Stryker restore all
/* v8 ignore stop */
10 changes: 1 addition & 9 deletions src/app/[locale]/lp/sign-up/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ import LandingPageMDX from '@/components/layouts/lp/MdxComponent';
import { getPageByLanguageAndPathStrict } from '@/lib/pages/api';
import { getStaticParams, getScopedI18n } from '@/i18n/server';
import I18nTaxonomy from '##/config/taxonomies/i18n';
import { getServerSession } from 'next-auth';
import ROUTES_ROOTS from '##/config/routes';
import { redirect } from 'next/navigation';
import { i18ns } from '##/config/i18n';

export async function generateMetadata({ params }: I18nPageProps) {
Expand All @@ -31,15 +28,10 @@ export function generateStaticParams() {
return getStaticParams();
}

export default async function Page({ params }: I18nPageProps) {
export default function Page({ params }: I18nPageProps) {
const language = params[I18nTaxonomy.LANGUAGE];
setStaticParamsLocale(language);

// {ToDo} Handle this guests only route middleware-side to avoid SSG break
// https://github.com/nextauthjs/next-auth/discussions/10628
const session = await getServerSession();
if (session) redirect(ROUTES_ROOTS.DASHBOARD);

const document = getPageByLanguageAndPathStrict({ path: 'lp/sign-up', lang: language }) as Page;
return <LandingPageMDX code={document.body.code} />;
}
Expand Down
10 changes: 9 additions & 1 deletion src/cache/generic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,28 @@ export function get(key: string) {
return GenericInMemoryCache.data[key]?.value;
}

export function set(key: string, data: Data, ttl: MsValue) {
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
export function set(key: string, data: Data, ttl: MsValue = 0) {
function setClock(key: string, ttl: MsValue) {
GenericInMemoryCache.data[key].clock = {
cachedAt: Date.now(),
ttl
};
}

function disposeClock(key: string) {
// @ts-expect-error - IDGAF lemme manipulate the RAM
GenericInMemoryCache.data[key].clock = undefined;
}

if (!GenericInMemoryCache.data[key]) GenericInMemoryCache.data[key] = {} as DataCacheEntry;

GenericInMemoryCache.data[key].value = typeof data === 'object' ? structuredClone(data) : data;

// eslint-disable-next-line @typescript-eslint/no-magic-numbers
if (ttl > 0) setClock(key, ttl);
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
else if (ttl < 0) disposeClock(key);
}

export async function getOrSet(key: string, data: () => Promise<Data>, ttl: MsValue) {
Expand Down
2 changes: 1 addition & 1 deletion src/config/Auth/__tests__/prebuild.authOptions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ describe('getDiscordProfilePicture (rate limited)', () => {
it('should return the cached ImageURL as fallback, given unhappy path (all is valid, but we got rate limited)', async () => {
vi.stubEnv('DISCORD_BOT_TOKEN', 'FAKE');
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
set(keysFactory.discordProfilePicture('FAKE_ID'), 'FAKE_URL', 660_000);
set(keysFactory.discordProfilePicture('FAKE_ID'), 'FAKE_URL');

const FAKE_ID = 'FAKE_ID';
const fakeDiscordApi: IDiscordApi = discordApi;
Expand Down
20 changes: 20 additions & 0 deletions src/lib/misc/getVipRouteShortcut.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/* v8 ignore start */
// Stryker disable all

import type { MaybeUndefined } from '@rtm/shared-types/CustomUtilityTypes';
import type { AppPath } from '@rtm/shared-types/Next';

import { VIP_SHORTCUTS } from '@/middleware';

import { getPathnameWithoutI18nFlag } from '../i18n';

function getVipRouteShortcut(pathname: AppPath): MaybeUndefined<AppPath> {
const currentRoute = getPathnameWithoutI18nFlag(pathname);
const vipShortcut = VIP_SHORTCUTS[currentRoute as keyof typeof VIP_SHORTCUTS] as MaybeUndefined<AppPath>;
return vipShortcut;
}

export default getVipRouteShortcut;

// Stryker restore all
/* v8 ignore stop */
4 changes: 2 additions & 2 deletions src/middleware.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/* v8 ignore start */
// Stryker disable all

import { APP_PROTECTED_PATHS, VIP_SHORTCUTS } from '##/config/auth';
import { withAuthMiddlewaresChain } from '@/middlewaresChain';
import { APP_PROTECTED_PATHS } from '##/config/auth';

export const config = { matcher: ['/((?!api|static|_next|.*\\..*).*)'] };

export { APP_PROTECTED_PATHS };
export { APP_PROTECTED_PATHS, VIP_SHORTCUTS };
export default withAuthMiddlewaresChain;

// Stryker restore all
Expand Down
28 changes: 23 additions & 5 deletions src/middlewares/withProtectedRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,45 @@
// Stryker disable all

import type { NextFetchEvent, NextMiddleware, NextRequest } from 'next/server';
import type { MiddlewareFactory } from '@rtm/shared-types/Next';
import type { MiddlewareFactory, AppPath } from '@rtm/shared-types/Next';
import type { NextRequestWithAuth } from 'next-auth/middleware';

import getVipRouteShortcut from '@/lib/misc/getVipRouteShortcut';
import isProtectedRoute from '@/lib/misc/isProtectedRoute';
import { mainMiddlewaresChain } from '@/middlewaresChain';
import { buildPathFromParts } from '@rtm/shared-lib/str';
import { getMaybeI18nFlagFromRequest } from '@/lib/next';
import { withAuth } from 'next-auth/middleware';
import ROUTES_ROOTS from '##/config/routes';

function authMiddleware(request: NextRequest) {
function protectedRoutesAuthMiddleware(request: NextRequest) {
const maybeI18nFlag = getMaybeI18nFlagFromRequest(request);
const i18nPrefix = maybeI18nFlag ? '/' + maybeI18nFlag : '';
const i18nPrefix = maybeI18nFlag ? maybeI18nFlag : '';

return withAuth(mainMiddlewaresChain, {
pages: { signIn: i18nPrefix + ROUTES_ROOTS.LANDING_PAGES + 'sign-up' }
pages: { signIn: buildPathFromParts(i18nPrefix, ROUTES_ROOTS.LANDING_PAGES, 'sign-up') }
});
}

function vipShortcutAuthMiddleware(request: NextRequest, shortcut: AppPath) {
const maybeI18nFlag = getMaybeI18nFlagFromRequest(request);
const i18nPrefix = maybeI18nFlag ? maybeI18nFlag : '';

return withAuth(mainMiddlewaresChain, {
callbacks: {
authorized: ({ token }) => !Boolean(token)
},
pages: { signIn: buildPathFromParts(i18nPrefix, shortcut) } // {ToDo} Rewrite this when we'll get rid of Next Auth in this project
});
}

const withProtectedRoutes: MiddlewareFactory = (next: NextMiddleware) => async (req: NextRequest, _next: NextFetchEvent) => {
const request = req as NextRequestWithAuth;
if (isProtectedRoute(request.nextUrl.pathname)) return authMiddleware(request)(request, _next);
if (isProtectedRoute(request.nextUrl.pathname)) return protectedRoutesAuthMiddleware(request)(request, _next);

const maybeShortcut = getVipRouteShortcut(req.nextUrl.pathname);
if (maybeShortcut !== undefined) return vipShortcutAuthMiddleware(request, maybeShortcut)(request, _next);

const res = await next(request, _next);
return res;
};
Expand Down

0 comments on commit 2e5c8d7

Please sign in to comment.