-
Notifications
You must be signed in to change notification settings - Fork 324
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: precached webui works in offline mode (#782)
This adds precache logic for Web UI which is executed when IPFS API client (or full node) starts. The main purpose is to make Web UI load instantly in Brave and work in offline environments How does it work? During the build .tar archives are placed in `add-on/dist/precache`. This makes them available to fetch over `*-extension://{extension-id}/dist/precache/*` URLs. Right now we have only one archive: a 22MB file with release version of Web UI. When IPFS client starts, preache logic is executed: 1. read Web UI CID from Companion's config 2. check if local repo contains mentioned CID. - if it is present, finish - if missing, continue to the next step 3. precache asynchronously: - fetch TAR archive from `*-extension://{extension-id}/dist/precache/*.tar` - pass entire directory tree as unpacked streams to ipfs.add - confirm produced CID is matching the one from step (1)
- Loading branch information
Showing
6 changed files
with
260 additions
and
60 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
'use strict' | ||
/* eslint-env browser, webextensions */ | ||
const pull = require('pull-stream/pull') | ||
const drain = require('pull-stream/sinks/drain') | ||
const toStream = require('it-to-stream') | ||
const tar = require('tar-stream') | ||
const CID = require('cids') | ||
const { webuiCid } = require('./state') | ||
|
||
const debug = require('debug') | ||
const log = debug('ipfs-companion:precache') | ||
log.error = debug('ipfs-companion:precache:error') | ||
|
||
const PRECACHE_ARCHIVES = [ | ||
{ tarPath: '/dist/precache/webui.tar', cid: webuiCid } | ||
] | ||
|
||
/** | ||
* Adds important assets such as Web UI to the local js-ipfs-repo. | ||
* This ensures they load instantly, even in offline environments. | ||
*/ | ||
module.exports = async (ipfs) => { | ||
for (const { cid, tarPath } of PRECACHE_ARCHIVES) { | ||
if (!await inRepo(ipfs, cid)) { | ||
await importTar(ipfs, tarPath, cid) | ||
} else { | ||
log(`${cid} already in local repo, skipping import`) | ||
} | ||
} | ||
} | ||
|
||
async function inRepo (ipfs, cid) { | ||
return new Promise((resolve, reject) => { | ||
let local = false | ||
pull( | ||
ipfs.refs.localPullStream(), | ||
drain(block => { | ||
if (block.ref === cid) { | ||
local = true | ||
return false // abort stream | ||
} | ||
}, () => resolve(local)) | ||
) | ||
}) | ||
} | ||
|
||
async function importTar (ipfs, tarPath, expectedCid) { | ||
const stream = toStream.readable(streamTar(tarPath)) | ||
// TODO: HTTP 404 means precache is disabled in the current runtime | ||
// (eg. in Firefox, due to https://github.com/ipfs-shipyard/ipfs-webui/issues/959) | ||
const untarAndAdd = tar.extract() | ||
|
||
const files = [] | ||
|
||
untarAndAdd.on('entry', (header, stream, next) => { | ||
// header is the tar header | ||
// stream is the content body (might be an empty stream) | ||
// call next when you are done with this entry | ||
|
||
if (header.type !== 'file') { | ||
// skip non-files | ||
stream.on('end', next) | ||
stream.resume() // drain stream | ||
return | ||
} | ||
|
||
files.push(new Promise((resolve, reject) => { | ||
let chunks = [] | ||
stream.on('data', data => chunks.push(data)) | ||
stream.on('end', () => { | ||
resolve({ path: header.name, content: Buffer.concat(chunks) }) | ||
chunks = null | ||
next() | ||
}) | ||
})) | ||
}) | ||
|
||
untarAndAdd.on('finish', async () => { | ||
const { version } = new CID(expectedCid) | ||
const opts = { cidVersion: version, pin: false, preload: false } | ||
const results = await ipfs.add(await Promise.all(files), opts) | ||
const root = results.find(e => e.hash === expectedCid) | ||
if (root) { | ||
log(`${tarPath} successfully precached`, root) | ||
} else { | ||
log.error('imported CID does not match expected one (requires new release with updated package.json)') | ||
} | ||
}) | ||
|
||
log(`importing ${tarPath} to js-ipfs-repo`) | ||
stream.pipe(untarAndAdd) | ||
} | ||
|
||
async function * streamTar (repoPath) { | ||
const response = await fetch(repoPath) | ||
const reader = response.body.getReader() | ||
try { | ||
while (true) { | ||
const { done, value } = await reader.read() | ||
if (done) return | ||
yield value | ||
} | ||
} finally { | ||
// Firefox only? https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamDefaultReader/releaseLock | ||
if (typeof reader.releaseLock === 'function') reader.releaseLock() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.