IPFS Support + Loading Improvements (Better local file handling) + Streamlined Collection View (#25)

* support ipfs:// urls!
misc: better error message for loading from http://
tabs: don't render pages/story/urls if hidden

* ipfs work: remove ww only use sw

* electron + ipfs: add ipfs support to electron app, using ipfs-core
- route ipfs requests to files proxy with 'ipfs=' query param

* file loading:
- add support for new Chrome native file system api with file handles, which allows persistent access to files vial file handles
- if window.showOpenFilePicker() is available use that, include file handle in load message
- support asking permissions for file handles, using similar workflow as google drive auth
- attempt to ask for file permission directly, show auth button if fails (user gestured required)
- replace auth modal with iframe replacement insert to ask for auth for google drive and local file

* ui overhaul for 1.3.0 / condense collection view:
- don't show navbar in collection view
- add logo to back to home page to location bar
- condense location bar buttons
- convert terms to general about dialog, available from collection context menu
- add info tab to collection view in place of drop-down info
- make <wr-coll-info> component, used in both collection list and info tab with additional details
- update to wabac.js 2.2.0

* update CHANGES.md
This commit is contained in:
Ilya Kreymer
2020-11-16 18:31:15 -08:00
committed by GitHub
parent e839400017
commit 228e9bdff6
15 changed files with 4533 additions and 614 deletions
+91 -17
View File
@@ -8,7 +8,7 @@ import fs from 'fs';
import { ArchiveResponse, Rewriter } from '@webrecorder/wabac/src/rewrite';
import { PassThrough } from 'stream';
import { PassThrough, Readable } from 'stream';
import mime from 'mime-types';
@@ -21,6 +21,10 @@ const REPLAY_PREFIX = STATIC_PREFIX + "wabac/";
const URL_RX = /([^\/]+)\/([\d]+)(?:\w\w_)?\/(.*)$/;
let IPFS = null;
let ipfs = null;
let initingIPFS = null;
// ============================================================================
class ElectronReplayApp
@@ -95,7 +99,7 @@ class ElectronReplayApp
app.commandLine.appendSwitch('ppapi-flash-path', this.pluginPath);
console.log('app path', this.appPath);
console.log('dir name', this.__dirname);
console.log('dir name', __dirname);
console.log('proj path', this.projPath);
console.log('plugin path', this.pluginPath);
@@ -134,7 +138,38 @@ class ElectronReplayApp
});
}
static async doInitIPFS() {
if (!IPFS) {
IPFS = require("ipfs-core");
}
ipfs = await IPFS.create({
//repo: "/tmp/test-ipfs"
init: {emptyRepo: true},
//preload: {enabled: false},
});
}
async initIPFS() {
if (!ipfs) {
try {
if (!initingIPFS) {
initingIPFS = ElectronReplayApp.doInitIPFS();
}
await initingIPFS;
} catch (e) {
console.warn(e);
}
}
return ipfs;
}
onAppReady() {
//ElectronReplayApp.doInitIPFS();
this.screenSize = screen.getPrimaryDisplay().workAreaSize;
app.on('web-contents-created', (event, contents) => {
@@ -213,27 +248,62 @@ class ElectronReplayApp
if (request.url.startsWith(__APP_FILE_SERVE_PREFIX__)) {
const parsedUrl = new URL(request.url);
const filename = parsedUrl.searchParams.get("filename");
console.log("file serve:", filename);
const stat = await fs.promises.lstat(filename);
if (!stat.isFile()) {
return this.notFound(filename, callback);
}
const size = stat.size;
const headers = {"Content-Length": "" + size, "Content-Type": "application/octet-stream"};
const ipfsCID = parsedUrl.searchParams.get("ipfs");
const headers = {"Content-Type": "application/octet-stream"};
const reqHeaders = new Headers(request.headers);
const {statusCode, start, end} = this.parseRange(reqHeaders, headers, size);
if (filename) {
console.log("file serve:", filename);
const data = request.method === "HEAD" ? null : fs.createReadStream(filename, {start, end});
const stat = await fs.promises.lstat(filename);
callback({statusCode, headers, data});
return;
if (!stat.isFile()) {
return this.notFound(filename, callback);
}
const size = stat.size;
const {statusCode, start, end} = this.parseRange(reqHeaders, headers, size);
const data = request.method === "HEAD" ? null : fs.createReadStream(filename, {start, end});
callback({statusCode, headers, data});
return;
} else if (ipfsCID) {
console.log("ipfs serve:", ipfsCID);
const ipfs = await this.initIPFS();
let size = 0;
for await (const file of ipfs.get(ipfsCID, {timeout: 20000, preload: false})) {
if (file.type !== "file") {
return this.notFound(ipfsCID, callback);
}
size = file.size;
break;
}
const {statusCode, start, end} = this.parseRange(reqHeaders, headers, size);
let data = null;
if (request.method === "GET") {
const offset = start || 0;
const length = end ? end - start + 1 : size;
data = Readable.from(ipfs.cat(ipfsCID, {offset, length}));
}
callback({statusCode, headers, data});
return;
} else {
return this.notFound("No Resource Specified", callback);
}
}
// possible 'live leak' attempt, just return not found
@@ -324,6 +394,9 @@ class ElectronReplayApp
const range = reqHeaders.get("range");
if (!range) {
if (headers) {
headers["Content-Length"] = "" + size;
}
return {statusCode};
}
@@ -337,6 +410,7 @@ class ElectronReplayApp
statusCode = 206;
if (headers) {
headers["Content-Range"] = `bytes ${start}-${end}/${size}`;
headers["Content-Length"] = `${end - start + 1}`;
}
return {statusCode, start, end};
}