2017-12-22 23:52:42 +00:00
// Inspired by https://github.com/ipfs/ipfs-service-worker which does not work with recent versions
2017-12-21 05:52:58 +00:00
// of js-ipfs.
/* global self, Response */
'use strict'
const IPFS = require ( 'ipfs' )
let node
// https://redfin.engineering/how-to-fix-the-refresh-button-when-using-service-workers-a8e27af6df68
self . skipWaiting ( )
self . addEventListener ( 'install' , ( event ) => {
console . log ( 'install step' )
event . waitUntil ( self . skipWaiting ( ) )
} )
self . addEventListener ( 'activate' , ( event ) => {
console . log ( 'activate step' )
event . waitUntil ( self . clients . claim ( ) )
} )
function initialize _ipfs ( ) {
return new Promise ( ( resolve , reject ) => {
node = new IPFS ( )
node . on ( 'ready' , ( ) => {
console . log ( 'js-ipfs node is ready' )
resolve ( )
} )
node . on ( 'error' , ( err ) => {
console . log ( 'js-ipfs node errored' , err )
reject ( err )
} )
} )
}
let ipfs _initialized
2017-12-22 06:16:53 +00:00
function response ( status _code , status _text , body , headers = { } ) {
// Return a promise for a response with the given HTTP status code, status text, and HTML body.
return new Promise ( ( resolve , reject ) => {
resolve (
new Response (
body ,
{
status : status _code ,
statusText : status _text ,
headers : headers
}
)
)
} )
}
2017-12-23 05:42:13 +00:00
function not _found _response ( ) {
return response (
404 , 'Not Found' ,
Buffer . from ( '<html><head><title>404 Not Found</title></head><body bgcolor="white"><center><h1>404 Not Found</h1></center><hr><center>Intergalactic</center></body></html>' ) ,
{ 'Content-Type' : 'text/html; charset=utf-8' }
)
}
2017-12-24 06:30:47 +00:00
function forbidden _response ( ) {
return response (
403 , 'Forbidden' ,
Buffer . from ( '<html><head><title>404 Not Found</title></head><body bgcolor="white"><center><h1>403 Forbidden</h1></center><hr><center>Intergalactic</center></body></html>' ) ,
{ 'Content-Type' : 'text/html; charset=utf-8' }
)
}
2017-12-21 05:52:58 +00:00
self . addEventListener ( 'fetch' , ( event ) => {
2017-12-23 05:42:13 +00:00
if ( ! event . request . url . startsWith ( self . location . origin ) ) {
2017-12-24 06:30:47 +00:00
return console . log ( 'fetch not in scope:' , event . request . url )
2017-12-21 05:52:58 +00:00
}
2017-12-23 05:42:13 +00:00
console . log ( 'handling fetch event:' , event . request . url )
2017-12-21 05:52:58 +00:00
2017-12-24 06:30:47 +00:00
const request _path = ( new URL ( event . request . url ) ) . pathname
2017-12-23 05:42:13 +00:00
2017-12-25 20:41:01 +00:00
if ( request _path == '/ipfs/bundle.js' || request _path == '/ipfs/fetcher.js' ) {
return console . log ( 'skipping bundles' )
2017-12-23 05:42:13 +00:00
}
2017-12-21 05:52:58 +00:00
2017-12-24 06:30:47 +00:00
// If this is a same-origin or CORS request, and it's not to a URL within the same base IPFS
// hash, then block it as forbidden. This in effect gives each base IPFS hash its own origin.
if ( event . request . mode == 'same-origin' || event . request . mode == 'cors' ) {
let matches = /^(\/ipfs\/\w*)/ . exec ( ( new URL ( event . request . referrer ) ) . pathname )
let referrer _base _path = matches [ 1 ]
if ( referrer _base _path && ! request _path . startsWith ( referrer _base _path ) ) {
console . log (
'denying ' + event . request . mode + ' request from referrer with different base hash:' ,
event . request . referrer
)
event . respondWith ( forbidden _response ( ) )
return
}
}
2017-12-21 05:52:58 +00:00
if ( ! ipfs _initialized ) {
ipfs _initialized = initialize _ipfs ( )
}
event . respondWith (
2017-12-22 06:16:53 +00:00
ipfs _initialized . then ( ( ) => {
2017-12-24 06:30:47 +00:00
return node . files . get ( request _path )
2017-12-22 06:52:52 +00:00
} ) . then ( ( files ) => {
// If there's just one result, return it.
if ( files . length == 1 && files [ 0 ] . content ) {
2017-12-23 04:45:13 +00:00
console . log ( 'found file content at' , files [ 0 ] . path )
2017-12-22 06:52:52 +00:00
return response ( 200 , 'OK' , files [ 0 ] . content )
}
// If there are multiple results (and so this appears to be a directory), but this URL
// doesn't have a trailing slash, redirect. This ensures relative paths work correctly
// for any resources pulled in on the page.
if ( files . length > 1 && event . request . url . slice ( - 1 ) != '/' ) {
2017-12-23 04:45:13 +00:00
console . log ( 'redirect to' , event . request . url + '/' )
2017-12-22 06:52:52 +00:00
return response ( 302 , 'Found' , '' , { 'Location' : event . request . url + '/' } )
}
// If there are multiple results, look for an index.html page to return.
for ( let file of files ) {
if ( file . path . endsWith ( '/index.html' ) ) {
2017-12-23 04:45:13 +00:00
console . log ( 'found file content at' , file . path )
2017-12-22 06:52:52 +00:00
return response ( 200 , 'OK' , file . content )
}
}
2017-12-23 04:45:13 +00:00
console . log ( 'file not found at' , event . request . url )
2017-12-23 05:42:13 +00:00
return not _found _response ( )
2017-12-22 06:52:52 +00:00
} ) . catch ( ( error ) => {
console . log ( error )
2017-12-23 07:16:48 +00:00
return not _found _response ( )
2017-12-21 05:52:58 +00:00
} )
)
} )