Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(server-portal): unregister past service workers with script injection #334

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions portal/server/app/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import blocklistChecker from "custom_blocklist_checker";
import { SuiNSResolver } from "@lib/suins";
import { WalrusSitesRouter } from "@lib/routing";
import { config } from "configuration_loader";
import { inject_unregister_service_worker_script } from "inject_unregister_sw_script";

if (config.enableSentry) {
// Only integrate Sentry on production.
Expand Down Expand Up @@ -43,7 +44,8 @@ export async function GET(req: Request) {
const walrusPath: string | null = getBlobIdLink(url.toString());
if (walrusPath) {
console.log(`Redirecting to aggregator url response: ${req.url} from ${objectIdPath}`);
return redirectToAggregatorUrlResponse(url, walrusPath);
const response = redirectToAggregatorUrlResponse(url, walrusPath);
return await inject_unregister_service_worker_script(response);
}

const parsedUrl = getSubdomainAndPath(url, Number(portalDomainNameLength));
Expand All @@ -52,11 +54,12 @@ export async function GET(req: Request) {

if (parsedUrl) {
if (blocklistChecker && await blocklistChecker.isBlocked(parsedUrl.subdomain)) {
return siteNotFound();
return await inject_unregister_service_worker_script(siteNotFound());
}

if (requestDomain == portalDomain && parsedUrl.subdomain) {
return await urlFetcher.resolveDomainAndFetchUrl(parsedUrl, null, blocklistChecker);
const response = await urlFetcher.resolveDomainAndFetchUrl(parsedUrl, null, blocklistChecker);
return await inject_unregister_service_worker_script(response);
}
}

Expand All @@ -71,8 +74,9 @@ export async function GET(req: Request) {
null,
blocklistChecker
);
return response;
return await inject_unregister_service_worker_script(response);
}

return new Response(`Resource at ${originalUrl} not found!`, { status: 404 });
const response404 = new Response(`Resource at ${originalUrl} not found!`, { status: 404 });
return await inject_unregister_service_worker_script(response404);
}
47 changes: 47 additions & 0 deletions portal/server/inject_unregister_sw_script.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

/**
* Adds a script to the response to unregister the service worker.
*
* This is a temporary workaround used to transition from serving the server-portal
* through the `walrus.site` domain.
* To prevent the service worker from intercepting with the portal's response,
* this script unregisters the old service worker.
*
* @param response - Any response returned by the portal.
* @returns a new response with the script injected.
*/
export async function inject_unregister_service_worker_script(response: Response): Promise<Response> {
const contentType = response.headers.get('content-type');
if (!contentType || !contentType.includes('text/html')) {
console.log('Skipping service worker unregistration script injection because the content type is not HTML.');
return response;
}

let responseBody = await response.text();
const script = `
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.getRegistrations().then(registrations => {
registrations.forEach(registration => {
console.log('Unregistering the walrus sites service-worker!');
registration.unregister();
location.reload();
});
});
}
</script>
`;

// Inject the script before the closing body tag.
responseBody = responseBody.replace('</body>', `${script}</body>`);

const responseWithUnregisterScript = new Response(responseBody, {
headers: response.headers,
status: response.status,
statusText: response.statusText,
});

return responseWithUnregisterScript;
}
Loading