Post session info to service worker instead of asking from sw (#2605)
post session info to service worker instead of asking from sw on each request
This commit is contained in:
parent
206a927f30
commit
074c555294
5 changed files with 84 additions and 42 deletions
|
|
@ -68,6 +68,7 @@ import { Create } from './client/create';
|
||||||
import { CreateSpaceModalRenderer } from '../features/create-space';
|
import { CreateSpaceModalRenderer } from '../features/create-space';
|
||||||
import { SearchModalRenderer } from '../features/search';
|
import { SearchModalRenderer } from '../features/search';
|
||||||
import { getFallbackSession } from '../state/sessions';
|
import { getFallbackSession } from '../state/sessions';
|
||||||
|
import { pushSessionToSW } from '../../sw-session';
|
||||||
|
|
||||||
export const createRouter = (clientConfig: ClientConfig, screenSize: ScreenSize) => {
|
export const createRouter = (clientConfig: ClientConfig, screenSize: ScreenSize) => {
|
||||||
const { hashRouter } = clientConfig;
|
const { hashRouter } = clientConfig;
|
||||||
|
|
@ -106,7 +107,8 @@ export const createRouter = (clientConfig: ClientConfig, screenSize: ScreenSize)
|
||||||
|
|
||||||
<Route
|
<Route
|
||||||
loader={() => {
|
loader={() => {
|
||||||
if (!getFallbackSession()) {
|
const session = getFallbackSession();
|
||||||
|
if (!session) {
|
||||||
const afterLoginPath = getAppPathFromHref(
|
const afterLoginPath = getAppPathFromHref(
|
||||||
getOriginBaseUrl(hashRouter),
|
getOriginBaseUrl(hashRouter),
|
||||||
window.location.href
|
window.location.href
|
||||||
|
|
@ -114,6 +116,7 @@ export const createRouter = (clientConfig: ClientConfig, screenSize: ScreenSize)
|
||||||
if (afterLoginPath) setAfterLoginRedirectPath(afterLoginPath);
|
if (afterLoginPath) setAfterLoginRedirectPath(afterLoginPath);
|
||||||
return redirect(getLoginPath());
|
return redirect(getLoginPath());
|
||||||
}
|
}
|
||||||
|
pushSessionToSW(session.baseUrl, session.accessToken);
|
||||||
return null;
|
return null;
|
||||||
}}
|
}}
|
||||||
element={
|
element={
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { createClient, MatrixClient, IndexedDBStore, IndexedDBCryptoStore } from
|
||||||
|
|
||||||
import { cryptoCallbacks } from './secretStorageKeys';
|
import { cryptoCallbacks } from './secretStorageKeys';
|
||||||
import { clearNavToActivePathStore } from '../app/state/navToActivePath';
|
import { clearNavToActivePathStore } from '../app/state/navToActivePath';
|
||||||
|
import { pushSessionToSW } from '../sw-session';
|
||||||
|
|
||||||
type Session = {
|
type Session = {
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
|
|
@ -53,6 +54,7 @@ export const clearCacheAndReload = async (mx: MatrixClient) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const logoutClient = async (mx: MatrixClient) => {
|
export const logoutClient = async (mx: MatrixClient) => {
|
||||||
|
pushSessionToSW();
|
||||||
mx.stopClient();
|
mx.stopClient();
|
||||||
try {
|
try {
|
||||||
await mx.logout();
|
await mx.logout();
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@ import App from './app/pages/App';
|
||||||
|
|
||||||
// import i18n (needs to be bundled ;))
|
// import i18n (needs to be bundled ;))
|
||||||
import './app/i18n';
|
import './app/i18n';
|
||||||
|
import { pushSessionToSW } from './sw-session';
|
||||||
|
import { getFallbackSession } from './app/state/sessions';
|
||||||
|
|
||||||
document.body.classList.add(configClass, varsClass);
|
document.body.classList.add(configClass, varsClass);
|
||||||
|
|
||||||
|
|
@ -25,16 +27,9 @@ if ('serviceWorker' in navigator) {
|
||||||
? `${trimTrailingSlash(import.meta.env.BASE_URL)}/sw.js`
|
? `${trimTrailingSlash(import.meta.env.BASE_URL)}/sw.js`
|
||||||
: `/dev-sw.js?dev-sw`;
|
: `/dev-sw.js?dev-sw`;
|
||||||
|
|
||||||
navigator.serviceWorker.register(swUrl);
|
navigator.serviceWorker.register(swUrl).then(() => {
|
||||||
navigator.serviceWorker.addEventListener('message', (event) => {
|
const session = getFallbackSession();
|
||||||
if (event.data?.type === 'token' && event.data?.responseKey) {
|
pushSessionToSW(session?.baseUrl, session?.accessToken);
|
||||||
// Get the token for SW.
|
|
||||||
const token = localStorage.getItem('cinny_access_token') ?? undefined;
|
|
||||||
event.source!.postMessage({
|
|
||||||
responseKey: event.data.responseKey,
|
|
||||||
token,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
10
src/sw-session.ts
Normal file
10
src/sw-session.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
export function pushSessionToSW(baseUrl?: string, accessToken?: string) {
|
||||||
|
if (!('serviceWorker' in navigator)) return;
|
||||||
|
if (!navigator.serviceWorker.controller) return;
|
||||||
|
|
||||||
|
navigator.serviceWorker.controller.postMessage({
|
||||||
|
type: 'setSession',
|
||||||
|
accessToken,
|
||||||
|
baseUrl,
|
||||||
|
});
|
||||||
|
}
|
||||||
94
src/sw.ts
94
src/sw.ts
|
|
@ -3,22 +3,64 @@
|
||||||
export type {};
|
export type {};
|
||||||
declare const self: ServiceWorkerGlobalScope;
|
declare const self: ServiceWorkerGlobalScope;
|
||||||
|
|
||||||
async function askForAccessToken(client: Client): Promise<string | undefined> {
|
self.addEventListener('install', () => {
|
||||||
return new Promise((resolve) => {
|
self.skipWaiting();
|
||||||
const responseKey = Math.random().toString(36);
|
});
|
||||||
const listener = (event: ExtendableMessageEvent) => {
|
|
||||||
if (event.data.responseKey !== responseKey) return;
|
self.addEventListener('activate', (event: ExtendableEvent) => {
|
||||||
resolve(event.data.token);
|
event.waitUntil(self.clients.claim());
|
||||||
self.removeEventListener('message', listener);
|
});
|
||||||
};
|
|
||||||
self.addEventListener('message', listener);
|
type SessionInfo = {
|
||||||
client.postMessage({ responseKey, type: 'token' });
|
accessToken: string;
|
||||||
|
baseUrl: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store session per client (tab)
|
||||||
|
*/
|
||||||
|
const sessions = new Map<string, SessionInfo>();
|
||||||
|
|
||||||
|
async function cleanupDeadClients() {
|
||||||
|
const activeClients = await self.clients.matchAll();
|
||||||
|
const activeIds = new Set(activeClients.map((c) => c.id));
|
||||||
|
|
||||||
|
Array.from(sessions.keys()).forEach((id) => {
|
||||||
|
if (!activeIds.has(id)) {
|
||||||
|
sessions.delete(id);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function fetchConfig(token?: string): RequestInit | undefined {
|
/**
|
||||||
if (!token) return undefined;
|
* Receive session updates from clients
|
||||||
|
*/
|
||||||
|
self.addEventListener('message', (event: ExtendableMessageEvent) => {
|
||||||
|
const client = event.source as Client | null;
|
||||||
|
if (!client) return;
|
||||||
|
|
||||||
|
const { type, accessToken, baseUrl } = event.data || {};
|
||||||
|
|
||||||
|
if (type !== 'setSession') return;
|
||||||
|
|
||||||
|
cleanupDeadClients();
|
||||||
|
|
||||||
|
if (typeof accessToken === 'string' && typeof baseUrl === 'string') {
|
||||||
|
sessions.set(client.id, { accessToken, baseUrl });
|
||||||
|
} else {
|
||||||
|
// Logout or invalid session
|
||||||
|
sessions.delete(client.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function validMediaRequest(url: string, baseUrl: string): boolean {
|
||||||
|
const downloadUrl = new URL('/_matrix/client/v1/media/download', baseUrl);
|
||||||
|
const thumbnailUrl = new URL('/_matrix/client/v1/media/thumbnail', baseUrl);
|
||||||
|
|
||||||
|
return url.startsWith(downloadUrl.href) || url.startsWith(thumbnailUrl.href);
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchConfig(token: string): RequestInit {
|
||||||
return {
|
return {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${token}`,
|
Authorization: `Bearer ${token}`,
|
||||||
|
|
@ -27,26 +69,16 @@ function fetchConfig(token?: string): RequestInit | undefined {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
self.addEventListener('activate', (event: ExtendableEvent) => {
|
|
||||||
event.waitUntil(clients.claim());
|
|
||||||
});
|
|
||||||
|
|
||||||
self.addEventListener('fetch', (event: FetchEvent) => {
|
self.addEventListener('fetch', (event: FetchEvent) => {
|
||||||
const { url, method } = event.request;
|
const { url, method } = event.request;
|
||||||
if (method !== 'GET') return;
|
|
||||||
if (
|
|
||||||
!url.includes('/_matrix/client/v1/media/download') &&
|
|
||||||
!url.includes('/_matrix/client/v1/media/thumbnail')
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
event.respondWith(
|
|
||||||
(async (): Promise<Response> => {
|
|
||||||
const client = await self.clients.get(event.clientId);
|
|
||||||
let token: string | undefined;
|
|
||||||
if (client) token = await askForAccessToken(client);
|
|
||||||
|
|
||||||
return fetch(url, fetchConfig(token));
|
if (method !== 'GET') return;
|
||||||
})()
|
if (!event.clientId) return;
|
||||||
);
|
|
||||||
|
const session = sessions.get(event.clientId);
|
||||||
|
if (!session) return;
|
||||||
|
|
||||||
|
if (!validMediaRequest(url, session.baseUrl)) return;
|
||||||
|
|
||||||
|
event.respondWith(fetch(url, fetchConfig(session.accessToken)));
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue