From 05c33bd94b7d9d61fe5b1175d249e6e7e4c55191 Mon Sep 17 00:00:00 2001 From: Aaron Presley <155617+AaronPresley@users.noreply.github.com> Date: Wed, 8 Feb 2023 16:35:43 -0800 Subject: [PATCH] I18N Caching (#6823) * Caching Logic Changes Enabled this function to change its cache value based on incoming paths value * Invalidate I18N Cache Invalidating the I18N cache when a user saves changes to their General settings * Removes deprecated useLocale location * Overriding the default getSchedule cache to have a revalidation time of 1 second * Update apps/web/pages/api/trpc/[trpc].ts * Updated cache values to match the comment --------- Co-authored-by: zomars --- apps/web/components/ImageUploader.tsx | 2 +- .../security/TwoFactorAuthSection.tsx | 3 +- apps/web/components/team/screens/Team.tsx | 3 +- apps/web/lib/hooks/useLocale.ts | 11 --- apps/web/pages/404.tsx | 3 +- apps/web/pages/api/trpc/[trpc].ts | 72 ++++++++++++------- apps/web/pages/auth/forgot-password/[id].tsx | 3 +- apps/web/pages/auth/forgot-password/index.tsx | 2 +- .../web/pages/settings/my-account/general.tsx | 4 +- 9 files changed, 57 insertions(+), 46 deletions(-) delete mode 100644 apps/web/lib/hooks/useLocale.ts diff --git a/apps/web/components/ImageUploader.tsx b/apps/web/components/ImageUploader.tsx index 650e39d50c..11ceeeb394 100644 --- a/apps/web/components/ImageUploader.tsx +++ b/apps/web/components/ImageUploader.tsx @@ -1,11 +1,11 @@ import { FormEvent, useCallback, useEffect, useState } from "react"; import Cropper from "react-easy-crop"; +import { useLocale } from "@calcom/lib/hooks/useLocale"; import { Button, Dialog, DialogClose, DialogContent, DialogTrigger } from "@calcom/ui"; import { Area, getCroppedImg } from "@lib/cropImage"; import { useFileReader } from "@lib/hooks/useFileReader"; -import { useLocale } from "@lib/hooks/useLocale"; import Slider from "@components/Slider"; diff --git a/apps/web/components/security/TwoFactorAuthSection.tsx b/apps/web/components/security/TwoFactorAuthSection.tsx index c53feff3bf..05cf9e6882 100644 --- a/apps/web/components/security/TwoFactorAuthSection.tsx +++ b/apps/web/components/security/TwoFactorAuthSection.tsx @@ -1,9 +1,8 @@ import { useState } from "react"; +import { useLocale } from "@calcom/lib/hooks/useLocale"; import { Badge, Button } from "@calcom/ui"; -import { useLocale } from "@lib/hooks/useLocale"; - import DisableTwoFactorModal from "./DisableTwoFactorModal"; import EnableTwoFactorModal from "./EnableTwoFactorModal"; diff --git a/apps/web/components/team/screens/Team.tsx b/apps/web/components/team/screens/Team.tsx index b00e5c3c31..c3527cc6fc 100644 --- a/apps/web/components/team/screens/Team.tsx +++ b/apps/web/components/team/screens/Team.tsx @@ -3,10 +3,9 @@ import Link from "next/link"; import { TeamPageProps } from "pages/team/[slug]"; import { WEBAPP_URL } from "@calcom/lib/constants"; +import { useLocale } from "@calcom/lib/hooks/useLocale"; import { Avatar } from "@calcom/ui"; -import { useLocale } from "@lib/hooks/useLocale"; - const md = new MarkdownIt("default", { html: true, breaks: true, linkify: true }); type TeamType = TeamPageProps["team"]; diff --git a/apps/web/lib/hooks/useLocale.ts b/apps/web/lib/hooks/useLocale.ts deleted file mode 100644 index 9e8ca501fd..0000000000 --- a/apps/web/lib/hooks/useLocale.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { useTranslation } from "next-i18next"; - -/** @deprecated use the one from `@calcom/lib/hooks/useLocale` */ -export const useLocale = () => { - const { i18n, t } = useTranslation("common"); - - return { - i18n, - t, - }; -}; diff --git a/apps/web/pages/404.tsx b/apps/web/pages/404.tsx index 44ac023d22..5aa7f89cfd 100644 --- a/apps/web/pages/404.tsx +++ b/apps/web/pages/404.tsx @@ -4,11 +4,10 @@ import { useRouter } from "next/router"; import { useEffect, useState } from "react"; import { COMPANY_NAME, DEVELOPER_DOCS, DOCS_URL, JOIN_SLACK, WEBSITE_URL } from "@calcom/lib/constants"; +import { useLocale } from "@calcom/lib/hooks/useLocale"; import { HeadSeo } from "@calcom/ui"; import { FiFileText, FiCheck, FiBookOpen, FiChevronRight } from "@calcom/ui/components/icon"; -import { useLocale } from "@lib/hooks/useLocale"; - import { ssgInit } from "@server/lib/ssg"; export default function Custom404() { diff --git a/apps/web/pages/api/trpc/[trpc].ts b/apps/web/pages/api/trpc/[trpc].ts index 7204fc2a30..4da664fc2b 100644 --- a/apps/web/pages/api/trpc/[trpc].ts +++ b/apps/web/pages/api/trpc/[trpc].ts @@ -30,34 +30,58 @@ export default trpcNext.createNextApiHandler({ * @link https://trpc.io/docs/caching#api-response-caching */ responseMeta({ ctx, paths, type, errors }) { - // assuming we have all our public routes in `viewer.public` - const allPublic = paths && paths.every((path) => path.startsWith("viewer.public.")); - // checking that no procedures errored - const allOk = errors.length === 0; - // checking we're doing a query request - const isQuery = type === "query"; + // Some helpers relevant to this function only + const ONE_DAY_IN_SECONDS = 60 * 60 * 24; + const TWO_HOURS_IN_SECONDS = 60 * 60 * 2; - // i18n response depends on request header - const nonCacheableQueries = ["viewer.public.i18n"]; - const isThereANonCacheableQuery = paths?.some((path) => nonCacheableQueries.includes(path)); - const isThereACacheableQuery = paths?.some((path) => !nonCacheableQueries.includes(path)); - if (isThereANonCacheableQuery && isThereACacheableQuery) { - console.warn( - "Cacheable and Non-cacheable queries are mixed in the same request. Not going to cache the request" - ); - } + // Our response to indicate no caching + const noCacheResponse = {}; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore ctx.req is available for SSR but not SSG - if (!!ctx?.req && allPublic && allOk && isQuery && !isThereANonCacheableQuery) { - // cache request for 1 day + revalidate once every 5 seconds - const ONE_DAY_IN_SECONDS = 60 * 60 * 24; - return { - headers: { - "cache-control": `s-maxage=5, stale-while-revalidate=${ONE_DAY_IN_SECONDS}`, - }, - }; + const isSSR = !!ctx?.req; + if (isSSR) { + return noCacheResponse; } - return {}; + + // No caching if we have a non-public path + // Assuming we have all our public routes in `viewer.public` + if (!paths || !paths.every((path) => path.startsWith("viewer.public."))) { + return noCacheResponse; + } + + // No caching if we have any procedures errored + if (errors.length !== 0) { + return noCacheResponse; + } + + // Never cache non-queries (aka mutations) + if (type !== "query") { + return noCacheResponse; + } + + // cache request for 1 day + revalidate once every 5 seconds + let cacheValue = `s-maxage=${ONE_DAY_IN_SECONDS}, stale-while-revalidate=5`; + + // Our cache can change depending on our current paths value. Since paths is an array, + // we want to create a map that can match potential paths with their desired cache value + const cacheRules = { + "viewer.public.i18n": `maxage=${TWO_HOURS_IN_SECONDS}, stale-while-revalidate=30`, + // Revalidation time here should be 1 second, per https://github.com/calcom/cal.com/pull/6823#issuecomment-1423215321 + "viewer.public.slots.getSchedule": `s-maxage=5, stale-while-revalidate=1`, + } as const; + + // Find which element above is an exact match for this group of paths + const matchedPath = paths.find((v) => v in cacheRules) as keyof typeof cacheRules; + + // Get the cache value of the matching element, if any + if (matchedPath) cacheValue = cacheRules[matchedPath]; + + // Finally we respond with our resolved cache value + return { + headers: { + "Cache-Control": cacheValue, + }, + }; }, }); diff --git a/apps/web/pages/auth/forgot-password/[id].tsx b/apps/web/pages/auth/forgot-password/[id].tsx index ba4fec7760..35450c2f59 100644 --- a/apps/web/pages/auth/forgot-password/[id].tsx +++ b/apps/web/pages/auth/forgot-password/[id].tsx @@ -6,11 +6,10 @@ import Link from "next/link"; import React, { useMemo } from "react"; import dayjs from "@calcom/dayjs"; +import { useLocale } from "@calcom/lib/hooks/useLocale"; import prisma from "@calcom/prisma"; import { Button, TextField } from "@calcom/ui"; -import { useLocale } from "@lib/hooks/useLocale"; - import AuthContainer from "@components/ui/AuthContainer"; type Props = { diff --git a/apps/web/pages/auth/forgot-password/index.tsx b/apps/web/pages/auth/forgot-password/index.tsx index cc389a382b..b995d33c4c 100644 --- a/apps/web/pages/auth/forgot-password/index.tsx +++ b/apps/web/pages/auth/forgot-password/index.tsx @@ -5,10 +5,10 @@ import Link from "next/link"; import { useRouter } from "next/router"; import React, { SyntheticEvent } from "react"; +import { useLocale } from "@calcom/lib/hooks/useLocale"; import { Button, EmailField } from "@calcom/ui"; import { getSession } from "@lib/auth"; -import { useLocale } from "@lib/hooks/useLocale"; import AuthContainer from "@components/ui/AuthContainer"; diff --git a/apps/web/pages/settings/my-account/general.tsx b/apps/web/pages/settings/my-account/general.tsx index a6034594a8..7de2e10bf7 100644 --- a/apps/web/pages/settings/my-account/general.tsx +++ b/apps/web/pages/settings/my-account/general.tsx @@ -69,7 +69,9 @@ const GeneralView = ({ localeProp, user }: GeneralViewProps) => { const { t } = useLocale(); const mutation = trpc.viewer.updateProfile.useMutation({ - onSuccess: () => { + onSuccess: async () => { + // Invalidate our previous i18n cache + await utils.viewer.public.i18n.invalidate(); reset(getValues()); showToast(t("settings_updated_successfully"), "success"); },