From dbe209c7fc70b53eb2bcc4f52de295d0a0041fdf Mon Sep 17 00:00:00 2001 From: m5r Date: Sat, 11 Jun 2022 16:13:00 +0200 Subject: [PATCH] purge phone calls and messages from cache when switching phone numbers or twilio account --- app/features/keypad/loaders/keypad.ts | 15 +++++++---- app/features/messages/loaders/messages.ts | 15 +++++++---- app/features/phone-calls/loaders/calls.ts | 23 ++++++++++------- app/features/settings/actions/phone.ts | 7 +++-- app/service-worker/cache-utils.ts | 31 ++++++++++++++++++++++- app/service-worker/fetch.ts | 6 +++++ app/service-worker/message.ts | 4 --- 7 files changed, 73 insertions(+), 28 deletions(-) diff --git a/app/features/keypad/loaders/keypad.ts b/app/features/keypad/loaders/keypad.ts index 549b4e9..c0d2d98 100644 --- a/app/features/keypad/loaders/keypad.ts +++ b/app/features/keypad/loaders/keypad.ts @@ -24,11 +24,16 @@ const loader: LoaderFunction = async ({ request }) => { where: { phoneNumberId: phoneNumber.id }, orderBy: { createdAt: Prisma.SortOrder.desc }, })); - return json({ - hasOngoingSubscription, - hasPhoneNumber, - lastRecipientCalled: lastCall?.recipient, - }); + return json( + { + hasOngoingSubscription, + hasPhoneNumber, + lastRecipientCalled: lastCall?.recipient, + }, + { + headers: { Vary: "Cookie" }, + }, + ); }; export default loader; diff --git a/app/features/messages/loaders/messages.ts b/app/features/messages/loaders/messages.ts index af2787c..de56e29 100644 --- a/app/features/messages/loaders/messages.ts +++ b/app/features/messages/loaders/messages.ts @@ -23,11 +23,16 @@ const loader: LoaderFunction = async ({ request }) => { const phoneNumber = await db.phoneNumber.findUnique({ where: { twilioAccountSid_isCurrent: { twilioAccountSid: twilio?.accountSid ?? "", isCurrent: true } }, }); - return json({ - hasPhoneNumber: Boolean(phoneNumber), - isFetchingMessages: phoneNumber?.isFetchingMessages ?? null, - conversations: await getConversations(phoneNumber), - }); + return json( + { + hasPhoneNumber: Boolean(phoneNumber), + isFetchingMessages: phoneNumber?.isFetchingMessages ?? null, + conversations: await getConversations(phoneNumber), + }, + { + headers: { Vary: "Cookie" }, + }, + ); }; export default loader; diff --git a/app/features/phone-calls/loaders/calls.ts b/app/features/phone-calls/loaders/calls.ts index 3fb4bff..630451e 100644 --- a/app/features/phone-calls/loaders/calls.ts +++ b/app/features/phone-calls/loaders/calls.ts @@ -44,15 +44,20 @@ const loader: LoaderFunction = async ({ request }) => { where: { phoneNumberId: phoneNumber.id }, orderBy: { createdAt: Prisma.SortOrder.desc }, }); - return json({ - hasOngoingSubscription, - hasPhoneNumber, - phoneCalls: phoneCalls.map((phoneCall) => ({ - ...phoneCall, - fromMeta: getPhoneNumberMeta(phoneCall.from), - toMeta: getPhoneNumberMeta(phoneCall.to), - })), - }); + return json( + { + hasOngoingSubscription, + hasPhoneNumber, + phoneCalls: phoneCalls.map((phoneCall) => ({ + ...phoneCall, + fromMeta: getPhoneNumberMeta(phoneCall.from), + toMeta: getPhoneNumberMeta(phoneCall.to), + })), + }, + { + headers: { Vary: "Cookie" }, + }, + ); }; export default loader; diff --git a/app/features/settings/actions/phone.ts b/app/features/settings/actions/phone.ts index cd7f85c..7d027c9 100644 --- a/app/features/settings/actions/phone.ts +++ b/app/features/settings/actions/phone.ts @@ -23,7 +23,6 @@ const action: ActionFunction = async ({ request }) => { return badRequest({ errorMessage }); } - console.log("formData._action", formData._action); switch (formData._action as Action) { case "setPhoneNumber": return setPhoneNumber(request, formData); @@ -128,9 +127,6 @@ async function setTwilioCredentials(request: Request, formData: unknown) { }; const [phoneNumbers] = await Promise.all([ twilioClient.incomingPhoneNumbers.list(), - setTwilioApiKeyQueue.add(`set twilio api key for accountSid=${twilioAccountSid}`, { - accountSid: twilioAccountSid, - }), db.twilioAccount.upsert({ where: { organizationId: organization.id }, create: { @@ -143,6 +139,9 @@ async function setTwilioCredentials(request: Request, formData: unknown) { }), ]); + setTwilioApiKeyQueue.add(`set twilio api key for accountSid=${twilioAccountSid}`, { + accountSid: twilioAccountSid, + }); await Promise.all( phoneNumbers.map(async (phoneNumber) => { const phoneNumberId = phoneNumber.sid; diff --git a/app/service-worker/cache-utils.ts b/app/service-worker/cache-utils.ts index bcdd9a4..11e0a01 100644 --- a/app/service-worker/cache-utils.ts +++ b/app/service-worker/cache-utils.ts @@ -17,6 +17,10 @@ export function isDocumentGetRequest(request: Request) { return request.method.toLowerCase() === "get" && request.mode === "navigate"; } +export function isMutationRequest(request: Request) { + return ["POST", "DELETE"].includes(request.method); +} + export function fetchAsset(event: FetchEvent): Promise { // stale-while-revalidate const url = new URL(event.request.url); @@ -172,5 +176,30 @@ export async function deleteCaches() { const allCaches = await caches.keys(); const cachesToDelete = allCaches.filter((cacheName) => cacheName !== ASSET_CACHE); await Promise.all(cachesToDelete.map((cacheName) => caches.delete(cacheName))); - console.debug("Caches deleted"); + console.debug("Old caches deleted"); +} + +export async function purgeMutatedLoaders(event: FetchEvent) { + const url = new URL(event.request.url); + const rootPathname = "/" + url.pathname.split("/")[1]; + const cache = await caches.open(DATA_CACHE); + const cachedLoaders = await cache.keys(); + + const loadersToDelete = cachedLoaders.filter((loader) => { + const cachedPathname = new URL(loader.url).pathname; + const shouldPurge = cachedPathname.startsWith(rootPathname); + + if (url.pathname === "/settings/phone") { + // changes phone number or twilio account credentials + // so purge messages and phone calls from cache + return ( + shouldPurge || + ["/messages", "/calls", "/keypad"].some((pathname) => cachedPathname.startsWith(pathname)) + ); + } + + return shouldPurge; + }); + await Promise.all(loadersToDelete.map((loader) => cache.delete(loader))); + console.debug("Purged loaders data starting with", rootPathname); } diff --git a/app/service-worker/fetch.ts b/app/service-worker/fetch.ts index bc81535..c04e0e8 100644 --- a/app/service-worker/fetch.ts +++ b/app/service-worker/fetch.ts @@ -5,6 +5,8 @@ import { isAssetRequest, isDocumentGetRequest, isLoaderRequest, + isMutationRequest, + purgeMutatedLoaders, } from "./cache-utils"; declare const self: ServiceWorkerGlobalScope; @@ -22,5 +24,9 @@ export default async function handleFetch(event: FetchEvent) { return fetchDocument(event); } + if (isMutationRequest(event.request)) { + await purgeMutatedLoaders(event); + } + return fetch(event.request); } diff --git a/app/service-worker/message.ts b/app/service-worker/message.ts index a734b85..4cbe7dd 100644 --- a/app/service-worker/message.ts +++ b/app/service-worker/message.ts @@ -51,9 +51,5 @@ async function purgeStaticAssets(assetsToCache: string[]) { const assetCache = await caches.open(ASSET_CACHE); const cachedAssets = await assetCache.keys(); const cachesToDelete = cachedAssets.filter((asset) => !assetsToCache.includes(new URL(asset.url).pathname)); - console.log( - "cachesToDelete", - cachesToDelete.map((c) => new URL(c.url).pathname), - ); await Promise.all(cachesToDelete.map((asset) => assetCache.delete(asset))); }