purge phone calls and messages from cache when switching phone numbers or twilio account
This commit is contained in:
parent
1e9b7a8aa2
commit
dbe209c7fc
@ -24,11 +24,16 @@ const loader: LoaderFunction = async ({ request }) => {
|
|||||||
where: { phoneNumberId: phoneNumber.id },
|
where: { phoneNumberId: phoneNumber.id },
|
||||||
orderBy: { createdAt: Prisma.SortOrder.desc },
|
orderBy: { createdAt: Prisma.SortOrder.desc },
|
||||||
}));
|
}));
|
||||||
return json<KeypadLoaderData>({
|
return json<KeypadLoaderData>(
|
||||||
|
{
|
||||||
hasOngoingSubscription,
|
hasOngoingSubscription,
|
||||||
hasPhoneNumber,
|
hasPhoneNumber,
|
||||||
lastRecipientCalled: lastCall?.recipient,
|
lastRecipientCalled: lastCall?.recipient,
|
||||||
});
|
},
|
||||||
|
{
|
||||||
|
headers: { Vary: "Cookie" },
|
||||||
|
},
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default loader;
|
export default loader;
|
||||||
|
@ -23,11 +23,16 @@ const loader: LoaderFunction = async ({ request }) => {
|
|||||||
const phoneNumber = await db.phoneNumber.findUnique({
|
const phoneNumber = await db.phoneNumber.findUnique({
|
||||||
where: { twilioAccountSid_isCurrent: { twilioAccountSid: twilio?.accountSid ?? "", isCurrent: true } },
|
where: { twilioAccountSid_isCurrent: { twilioAccountSid: twilio?.accountSid ?? "", isCurrent: true } },
|
||||||
});
|
});
|
||||||
return json<MessagesLoaderData>({
|
return json<MessagesLoaderData>(
|
||||||
|
{
|
||||||
hasPhoneNumber: Boolean(phoneNumber),
|
hasPhoneNumber: Boolean(phoneNumber),
|
||||||
isFetchingMessages: phoneNumber?.isFetchingMessages ?? null,
|
isFetchingMessages: phoneNumber?.isFetchingMessages ?? null,
|
||||||
conversations: await getConversations(phoneNumber),
|
conversations: await getConversations(phoneNumber),
|
||||||
});
|
},
|
||||||
|
{
|
||||||
|
headers: { Vary: "Cookie" },
|
||||||
|
},
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default loader;
|
export default loader;
|
||||||
|
@ -44,7 +44,8 @@ const loader: LoaderFunction = async ({ request }) => {
|
|||||||
where: { phoneNumberId: phoneNumber.id },
|
where: { phoneNumberId: phoneNumber.id },
|
||||||
orderBy: { createdAt: Prisma.SortOrder.desc },
|
orderBy: { createdAt: Prisma.SortOrder.desc },
|
||||||
});
|
});
|
||||||
return json<PhoneCallsLoaderData>({
|
return json<PhoneCallsLoaderData>(
|
||||||
|
{
|
||||||
hasOngoingSubscription,
|
hasOngoingSubscription,
|
||||||
hasPhoneNumber,
|
hasPhoneNumber,
|
||||||
phoneCalls: phoneCalls.map((phoneCall) => ({
|
phoneCalls: phoneCalls.map((phoneCall) => ({
|
||||||
@ -52,7 +53,11 @@ const loader: LoaderFunction = async ({ request }) => {
|
|||||||
fromMeta: getPhoneNumberMeta(phoneCall.from),
|
fromMeta: getPhoneNumberMeta(phoneCall.from),
|
||||||
toMeta: getPhoneNumberMeta(phoneCall.to),
|
toMeta: getPhoneNumberMeta(phoneCall.to),
|
||||||
})),
|
})),
|
||||||
});
|
},
|
||||||
|
{
|
||||||
|
headers: { Vary: "Cookie" },
|
||||||
|
},
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default loader;
|
export default loader;
|
||||||
|
@ -23,7 +23,6 @@ const action: ActionFunction = async ({ request }) => {
|
|||||||
return badRequest({ errorMessage });
|
return badRequest({ errorMessage });
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("formData._action", formData._action);
|
|
||||||
switch (formData._action as Action) {
|
switch (formData._action as Action) {
|
||||||
case "setPhoneNumber":
|
case "setPhoneNumber":
|
||||||
return setPhoneNumber(request, formData);
|
return setPhoneNumber(request, formData);
|
||||||
@ -128,9 +127,6 @@ async function setTwilioCredentials(request: Request, formData: unknown) {
|
|||||||
};
|
};
|
||||||
const [phoneNumbers] = await Promise.all([
|
const [phoneNumbers] = await Promise.all([
|
||||||
twilioClient.incomingPhoneNumbers.list(),
|
twilioClient.incomingPhoneNumbers.list(),
|
||||||
setTwilioApiKeyQueue.add(`set twilio api key for accountSid=${twilioAccountSid}`, {
|
|
||||||
accountSid: twilioAccountSid,
|
|
||||||
}),
|
|
||||||
db.twilioAccount.upsert({
|
db.twilioAccount.upsert({
|
||||||
where: { organizationId: organization.id },
|
where: { organizationId: organization.id },
|
||||||
create: {
|
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(
|
await Promise.all(
|
||||||
phoneNumbers.map(async (phoneNumber) => {
|
phoneNumbers.map(async (phoneNumber) => {
|
||||||
const phoneNumberId = phoneNumber.sid;
|
const phoneNumberId = phoneNumber.sid;
|
||||||
|
@ -17,6 +17,10 @@ export function isDocumentGetRequest(request: Request) {
|
|||||||
return request.method.toLowerCase() === "get" && request.mode === "navigate";
|
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<Response> {
|
export function fetchAsset(event: FetchEvent): Promise<Response> {
|
||||||
// stale-while-revalidate
|
// stale-while-revalidate
|
||||||
const url = new URL(event.request.url);
|
const url = new URL(event.request.url);
|
||||||
@ -172,5 +176,30 @@ export async function deleteCaches() {
|
|||||||
const allCaches = await caches.keys();
|
const allCaches = await caches.keys();
|
||||||
const cachesToDelete = allCaches.filter((cacheName) => cacheName !== ASSET_CACHE);
|
const cachesToDelete = allCaches.filter((cacheName) => cacheName !== ASSET_CACHE);
|
||||||
await Promise.all(cachesToDelete.map((cacheName) => caches.delete(cacheName)));
|
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);
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,8 @@ import {
|
|||||||
isAssetRequest,
|
isAssetRequest,
|
||||||
isDocumentGetRequest,
|
isDocumentGetRequest,
|
||||||
isLoaderRequest,
|
isLoaderRequest,
|
||||||
|
isMutationRequest,
|
||||||
|
purgeMutatedLoaders,
|
||||||
} from "./cache-utils";
|
} from "./cache-utils";
|
||||||
|
|
||||||
declare const self: ServiceWorkerGlobalScope;
|
declare const self: ServiceWorkerGlobalScope;
|
||||||
@ -22,5 +24,9 @@ export default async function handleFetch(event: FetchEvent) {
|
|||||||
return fetchDocument(event);
|
return fetchDocument(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isMutationRequest(event.request)) {
|
||||||
|
await purgeMutatedLoaders(event);
|
||||||
|
}
|
||||||
|
|
||||||
return fetch(event.request);
|
return fetch(event.request);
|
||||||
}
|
}
|
||||||
|
@ -51,9 +51,5 @@ async function purgeStaticAssets(assetsToCache: string[]) {
|
|||||||
const assetCache = await caches.open(ASSET_CACHE);
|
const assetCache = await caches.open(ASSET_CACHE);
|
||||||
const cachedAssets = await assetCache.keys();
|
const cachedAssets = await assetCache.keys();
|
||||||
const cachesToDelete = cachedAssets.filter((asset) => !assetsToCache.includes(new URL(asset.url).pathname));
|
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)));
|
await Promise.all(cachesToDelete.map((asset) => assetCache.delete(asset)));
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user