Skip to content

Instantly share code, notes, and snippets.

@freelancing-solutions
Last active March 9, 2021 17:58
Show Gist options
  • Save freelancing-solutions/8891501dbad18d85568380781d3c50f1 to your computer and use it in GitHub Desktop.
Save freelancing-solutions/8891501dbad18d85568380781d3c50f1 to your computer and use it in GitHub Desktop.
writing a custom service worker
// Incrementing OFFLINE_VERSION will kick off the install event and force
// previously cached resources to be updated from the network.
const OFFLINE_VERSION = 1;
const CACHE_NAME = 'FREELANCE-PROFILESS';
const website_base_url = 'http://localhost';
// Customize this with your URL.
// TODO- use Flask Assets to bundle some of the adminLTE css files and js files togather,
// TODO- also bundle adminLTE Files with flask bundler...
// NOTE- Never Cache URLS that require Authorization
// Load all other URLS in this array, you have to manually insert them here one by one or use bundlers
// for your static assets that way you can preload them easily here...
const CACHE_URLS = [ '/' ];
// ***********************************************************************************************//
// Global Variable to hold the value of the authorization token
let auth_token = "";
let cache = self.caches;
// NOTE: Installing Service worker
self.addEventListener('install', (event) => {
event.waitUntil((async () => {
// ON Service worker installation is where i load to cache
await cache.open(CACHE_NAME).then(cache => cache.addAll(CACHE_URLS));
})());
});
// Activating Worker
self.addEventListener('activate', (event) => {
event.waitUntil((async () => {
// Enable navigation preload if it's supported.
// See https://developers.google.com/web/updates/2017/02/navigation-preload
if ('navigationPreload' in self.registration) {
await self.registration.navigationPreload.enable();
}
})());
// Claiming Control of page
self.clients.claim();
});
// Listening to fetch events to handle requests and Authorization
// TODO- consider using the fetch event handler to dispatch messages back to the page triggering the events
self.addEventListener('fetch', (event) => {
// Checks if the request is included in our cache if yes returns the cached request
let check_cache_hit = async event => {
let matched_response = cache.match(event.request);
if (matched_response){return matched_response}else{await cache.add(event.request); return false}
}
// handling fetch events
request_url = String(event.request.url);
// console.log('request url', request_url);
if (event.request.method == "POST"){
// NOTE- This handles adding auth headers to request
return event.respondWith( (async () => await fetch(event.request))())
}
if (event.request.method == "DELETE"){
// NOTE- This handles adding auth headers to request
return event.respondWith( (async () => await fetch(event.request))())
}
if (event.request.method == "PUT"){
// NOTE- This handles adding auth headers to request
return event.respondWith( (async () => await fetch(event.request))())
}
// if its service worker request fetch a fresh copy
if ((event.request.method == "GET") && (request_url.includes('sw.js'))) {
return event.respondWith( (async () => await fetch(event.request))())
}
// if its for static assets fetch first from cache if failed fetch fresh copy
if ((event.request.method == "GET") && (request_url.includes('static'))){
return event.respondWith((async () => {return await event.preloadResponse || await check_cache_hit(event) || await fetch(event.request)})())
}
// if its a GET request to another domain fetch a fresh copy
// if its service worker request fetch a fresh copy
if ((event.request.method == "GET") && (!request_url.includes(website_base_url))){
return event.respondWith( (async () => await fetch(event.request))())
}
// all other requests fetch securely from backend
return event.respondWith((async () => {return await handle_auth_headers(event)})())
});
// NOTE: This is where i actually insert auth headers
let handle_auth_headers = async event => {
let headers = new Headers();
event.request.headers.forEach((val, key) => {
headers.append(key, val);
});
// Add ID token to header.
headers.append("x-access-token", auth_token);
console.log("Auth-Token Inserted on Headers : ", auth_token);
// always user cors in order to enable headers modification
let request = new Request(event.request.url, {
method: event.request.method,
headers: headers,
mode: "cors",
credentials: event.request.credentials,
cache: event.request.cache,
redirect:event.request.redirect,
referrer: event.request.referrer,
body: event.request.body,
bodyUsed: event.request.bodyUsed,
context: event.request.context
});
console.log("fetching this request securely", request.url);
return await fetch(request);
}
//********************************************************************************************* */
// Message Dispatcher
let messaging = {
// This handler should handle all communication from the app to the
// service worker -> from the service worker to the outside world
messages_handler : function(e){
this.dispatch_message_to_sender = function(e, message){e.source.postMessage(message)};
if (e.data && e.data.type === "auth-token"){
// console.log("setting token", e.data.token);
auth_token = e.data.token;
if ((auth_token !== "") && (auth_token !== "undefined")){
this.dispatch_message_to_sender(e,{type: "auth-token",message: "Token-Received"})
}else{
auth_token = "";
this.dispatch_message_to_sender(e,{type: "auth-token",message: "Not-Logged-IN"})
}
}
console.log("Service Worker Receiving this message : ", e.data);
},
//********************************************************************************************* *//
// PUSH Messages Handler
push_handler : function(e){
console.log("push dispatching here");
// let json_message = e.data.json();
// let blob_data = e.data.blob();
let text_message = e.data.text();
// console.log("json", json_message);
// console.log("blob", blob_data);
console.log('text message', text_message);
},
// TODO- dispatch PUSH Messages to App.js push message handler
// The handler should decipher the message and decide as to where its destined
//********************************************************************************************* */
// Sync messages handlers
sync_handler : function(e){
console.log("SYNC DISPATCHER", e);
},
init : function(){
self.addEventListener('message', this.messages_handler);
self.addEventListener('sync', this.sync_handler);
self.addEventListener('push', this.push_handler)
}
}
messaging.init();
@freelancing-solutions
Copy link
Author

Custom Cache First Service Worker With Planned Support for Push and Messaging

The service worker is built with integrated authentication based on JWT Authentication and supports message based communication
between Document API and Service Worker , to pass around authentication details.

Authentication is supported by passing header information
by modifying request headers of requests that may need authentication:

The Function handle_auth_headers above takes care of inserting header information
to authenticate calls to the back-end server.

In order for header modification to succeed however the request **mode needs to be set as "cors" ** any other mode will
mean that request headers can not be modified and you will be unable to insert authentication tokens from the service worker.
hence this is why every request mode is set to cors and all other options are left as is.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment