Modify anvil http-headers to broaden the scope of service workers?

Dear Anvil team,

I was looking into a way of integrating push notifications and came across the work of Chad . I tried setting it up and to use Firebase to send push notifications but it seems that the push notifications have some inconsistent behaviour.

To integrate them with Firebase, one needs to employ a service worker to handle the messages. However, when we upload it to the assets directory, it is not served from the root, but from a different location. According to the service worker documentation, it only affects pages that are within the same folder as the service worker. The scope can be increased, but for that a line in the http-header response of the server would have to be added.

Or do you think using routing would help somehow?

We would like to ask you to have a look into this and tell us your opinion. I would like try if it is possible to install an anvil app as pwa and then deploy some badges or notifications. I am working on a small board game for two players and would like to use push notifications to notify the opponent that it is their turn :slight_smile:
Thank you :slight_smile:

possible response header to extend the scope of service workers

Service-Worker-Allowed: /
The current response headers look like this, at least from what I could get via 
HTTP/1.1 302 Found
content-length: 0
location: https://the-app-location.anvil.app/
cache-control: no-cache
1 Like

You can already do this.

In Anvil, you can serve anything from any route, except for the _ namespace, which is reserved.
e.g. your theme assets are served from _/theme

Just create an @anvil.server.route

e.g. (in a server module)

import anvil.server
from anvil.files import data_files


@anvil.server.route("/assets/sw.js")
def sw(**params):
    headers = {
        "Content-Type": "text/javascript; charset=utf-8",
        "X-Content-Type-Options": "nosniff",
        "Cache-Control": "public, max-age=0, must-revalidate",
        "Service-Worker-Allowed": "/",
    }

    with open(data_files["sw.js"]) as f:
        return anvil.server.HttpResponse(body=f.read(), headers=headers)

Here i’ve used the files service, since the server module doesn’t have access to our app’s files without doing a network request.

I added this in native libraries


<script type="module">
    if ("serviceWorker" in navigator) {
        const url = new URL("./assets/sw.js", document.baseURI);
        navigator.serviceWorker
            .register(url, { scope: "/", updateViaCache: "none" })
            .then((sw) => {
                console.log("%cService Worker Registered", "color:green", sw);
            })
            .catch((error) => {
                console.error("Service worker registration failed:", error);
            });
    }
</script>



sw.js is a simple javascript service worker that just does some logging

// sw.js

self.addEventListener("install", (e) => {
    console.log("%cService Worker installed with scope:", "color: green", self.registration.scope);
});

self.addEventListener("activate", (e) => {
    console.log("%cService Worker activated", "color: green");
});

self.addEventListener("fetch", (e) => {
    console.log("%cService Worker fetching", "color: green", e.request.url);
});

Here’s a clone link.
If you run the app and open the browser console, you should see the service worker logs in green.

4 Likes

@stucork You are a genius! I think it just works :smiley:
I ran a few dirty quick tests, but it started behaving as expected. I didnt know about the anvil.server.route … I was searching among the opensource anvil server command line options and couldnt find anything :))) I will notify Chad to verify that this is the missin link. Thank you so so much :slight_smile:

1 Like

@stucork I had a look at the documentation of anvil.server and cannot find the anvil.server.route there. I searched forums and it seems something relevant to Python3.10
However it seems documented here.

I am just writing this post to bring this inconsistency to attention :slight_smile: Thanks a lot again. Petr