add electron build!
- use webpack to build electron-main and electron-preload - intercept 'https' protocol to run on 'https://replayweb.page' in app - serve local files from 'https://files.replayweb.page' - support flash in with service worker replay: catch any fallthrough urls and load via intercept with render process callback - add icons for electron build
This commit is contained in:
@@ -10,3 +10,6 @@ local-examples/
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
.sass-cache/
|
.sass-cache/
|
||||||
_site/
|
_site/
|
||||||
|
dist/
|
||||||
|
!dist/frontend.js
|
||||||
|
!dist/embed.js
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
Vendored
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
!function(t,e){for(var s in e)t[s]=e[s]}(self,function(t){var e={};function s(n){if(e[n])return e[n].exports;var r=e[n]={i:n,l:!1,exports:{}};return t[n].call(r.exports,r,r.exports,s),r.l=!0,r.exports}return s.m=t,s.c=e,s.d=function(t,e,n){s.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},s.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},s.t=function(t,e){if(1&e&&(t=s(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(s.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var r in t)s.d(n,r,function(e){return t[e]}.bind(null,r));return n},s.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return s.d(e,"a",e),e},s.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},s.p="/dist/",s(s.s=107)}({0:function(t,e,s){"use strict";s.d(e,"c",(function(){return d.e})),s.d(e,"d",(function(){return N})),s.d(e,"b",(function(){return C})),s.d(e,"a",(function(){return E}));var n=s(5),r=s(1);function i(t,e){const{element:{content:s},parts:n}=t,r=document.createTreeWalker(s,133,null,!1);let i=a(n),o=n[i],c=-1,l=0;const u=[];let d=null;for(;r.nextNode();){c++;const t=r.currentNode;for(t.previousSibling===d&&(d=null),e.has(t)&&(u.push(t),null===d&&(d=t)),null!==d&&l++;void 0!==o&&o.index===c;)o.index=null!==d?-1:o.index-l,i=a(n,i),o=n[i]}u.forEach(t=>t.parentNode.removeChild(t))}const o=t=>{let e=11===t.nodeType?0:1;const s=document.createTreeWalker(t,133,null,!1);for(;s.nextNode();)e++;return e},a=(t,e=-1)=>{for(let s=e+1;s<t.length;s++){const e=t[s];if(Object(r.d)(e))return s}return-1};var c=s(9),l=s(8),u=s(13),d=s(6);
|
!function(t,e){for(var s in e)t[s]=e[s]}(self,function(t){var e={};function s(n){if(e[n])return e[n].exports;var r=e[n]={i:n,l:!1,exports:{}};return t[n].call(r.exports,r,r.exports,s),r.l=!0,r.exports}return s.m=t,s.c=e,s.d=function(t,e,n){s.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},s.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},s.t=function(t,e){if(1&e&&(t=s(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(s.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var r in t)s.d(n,r,function(e){return t[e]}.bind(null,r));return n},s.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return s.d(e,"a",e),e},s.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},s.p="/dist/",s(s.s=108)}({0:function(t,e,s){"use strict";s.d(e,"c",(function(){return d.e})),s.d(e,"d",(function(){return N})),s.d(e,"b",(function(){return C})),s.d(e,"a",(function(){return E}));var n=s(5),r=s(1);function i(t,e){const{element:{content:s},parts:n}=t,r=document.createTreeWalker(s,133,null,!1);let i=a(n),o=n[i],c=-1,l=0;const u=[];let d=null;for(;r.nextNode();){c++;const t=r.currentNode;for(t.previousSibling===d&&(d=null),e.has(t)&&(u.push(t),null===d&&(d=t)),null!==d&&l++;void 0!==o&&o.index===c;)o.index=null!==d?-1:o.index-l,i=a(n,i),o=n[i]}u.forEach(t=>t.parentNode.removeChild(t))}const o=t=>{let e=11===t.nodeType?0:1;const s=document.createTreeWalker(t,133,null,!1);for(;s.nextNode();)e++;return e},a=(t,e=-1)=>{for(let s=e+1;s<t.length;s++){const e=t[s];if(Object(r.d)(e))return s}return-1};var c=s(9),l=s(8),u=s(13),d=s(6);
|
||||||
/**
|
/**
|
||||||
* @license
|
* @license
|
||||||
* Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
|
* Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
|
||||||
@@ -65,7 +65,7 @@ const n=`{{lit-${String(Math.random()).slice(2)}}}`,r=`\x3c!--${n}--\x3e`,i=new
|
|||||||
* subject to an additional IP rights grant found at
|
* subject to an additional IP rights grant found at
|
||||||
* http://polymer.github.io/PATENTS.txt
|
* http://polymer.github.io/PATENTS.txt
|
||||||
*/
|
*/
|
||||||
const n=new WeakMap,r=t=>(...e)=>{const s=t(...e);return n.set(s,!0),s},i=t=>"function"==typeof t&&n.has(t)},107:function(t,e,s){"use strict";s.r(e);var n=s(0);class r extends n.a{constructor(){super(),this.replayBase="http://localhost:9990/",this.view="replay",this.ts="",this.url="",this.config="",this.coll="",this.paramString=null,this.deepLink=!1}static get properties(){return{url:{type:String},ts:{type:String},source:{type:String},replayBase:{type:String},title:{type:String},coll:{type:String},config:{type:String},paramString:{type:String},hashString:{type:String},deepLink:{type:Boolean}}}firstUpdated(){window.addEventListener("message",t=>{const e=this.renderRoot.querySelector("iframe");if(e&&t.source===e.contentWindow){if(t.data.title&&(this.title=t.data.title),console.log(t.data),!this.deepLink)return;const e=new URLSearchParams({url:t.data.url,ts:t.data.ts}),s=new URL(window.location.href);s.hash="#"+e.toString(),window.history.replaceState({},"",s)}}),this.deepLink&&(this.updateFromHash(),window.addEventListener("hashchange",t=>this.updateFromHash()))}updateFromHash(){const t=new URLSearchParams(window.location.hash.slice(1));t.has("url")&&(this.url=t.get("url")),t.has("ts")&&(this.ts=t.get("ts"))}updated(t){(t.has("url")||t.has("ts")||t.has("source"))&&(this.paramString=new URLSearchParams({source:this.source,customColl:this.coll,config:this.config,embed:"true"}).toString(),this.hashString=new URLSearchParams({url:this.url,ts:this.ts,view:this.view}).toString())}static get styles(){return n.b`
|
const n=new WeakMap,r=t=>(...e)=>{const s=t(...e);return n.set(s,!0),s},i=t=>"function"==typeof t&&n.has(t)},108:function(t,e,s){"use strict";s.r(e);var n=s(0);class r extends n.a{constructor(){super(),this.replayBase="http://localhost:9990/",this.view="replay",this.ts="",this.url="",this.config="",this.coll="",this.paramString=null,this.deepLink=!1}static get properties(){return{url:{type:String},ts:{type:String},source:{type:String},replayBase:{type:String},title:{type:String},coll:{type:String},config:{type:String},paramString:{type:String},hashString:{type:String},deepLink:{type:Boolean}}}firstUpdated(){window.addEventListener("message",t=>{const e=this.renderRoot.querySelector("iframe");if(e&&t.source===e.contentWindow){if(t.data.title&&(this.title=t.data.title),console.log(t.data),!this.deepLink)return;const e=new URLSearchParams({url:t.data.url,ts:t.data.ts}),s=new URL(window.location.href);s.hash="#"+e.toString(),window.history.replaceState({},"",s)}}),this.deepLink&&(this.updateFromHash(),window.addEventListener("hashchange",t=>this.updateFromHash()))}updateFromHash(){const t=new URLSearchParams(window.location.hash.slice(1));t.has("url")&&(this.url=t.get("url")),t.has("ts")&&(this.ts=t.get("ts"))}updated(t){(t.has("url")||t.has("ts")||t.has("source"))&&(this.paramString=new URLSearchParams({source:this.source,customColl:this.coll,config:this.config,embed:"true"}).toString(),this.hashString=new URLSearchParams({url:this.url,ts:this.ts,view:this.view}).toString())}static get styles(){return n.b`
|
||||||
iframe {
|
iframe {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|||||||
Vendored
+12
-12
File diff suppressed because one or more lines are too long
+65
-3
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "replaywebpage",
|
"name": "ReplayWeb.page",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "Serverless Web Archive Replay",
|
"description": "Serverless Web Archive Replay",
|
||||||
"main": "index.js",
|
"main": "dist/electron.js",
|
||||||
"repository": "https://github.com/webrecorder/replayweb.page",
|
"repository": "https://github.com/webrecorder/replayweb.page",
|
||||||
"author": "Ilya Kreymer <ikreymer@gmail.com>",
|
"author": "Ilya Kreymer <ikreymer@gmail.com>",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
@@ -18,6 +18,8 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"css-loader": "^3.5.3",
|
"css-loader": "^3.5.3",
|
||||||
|
"electron": "^9.0.0",
|
||||||
|
"electron-builder": "^22.6.1",
|
||||||
"mini-css-extract-plugin": "^0.9.0",
|
"mini-css-extract-plugin": "^0.9.0",
|
||||||
"node-sass": "^4.14.0",
|
"node-sass": "^4.14.0",
|
||||||
"raw-loader": "^4.0.1",
|
"raw-loader": "^4.0.1",
|
||||||
@@ -32,6 +34,66 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "webpack --mode production",
|
"build": "webpack --mode production",
|
||||||
"build-dev": "webpack --mode development",
|
"build-dev": "webpack --mode development",
|
||||||
"start-dev": "webpack-dev-server --mode development"
|
"start-dev": "webpack-dev-server --mode development",
|
||||||
|
"pack": "CSC_IDENTITY_AUTO_DISCOVERY=false electron-builder --publish never",
|
||||||
|
"dist": "electron-builder",
|
||||||
|
"start-electron": "NODE_ENV=development electron ./dist/electron.js",
|
||||||
|
"build-electron": "yarn run build && yarn run pack"
|
||||||
|
},
|
||||||
|
"build": {
|
||||||
|
"productName": "ReplayWeb.page",
|
||||||
|
"asar": true,
|
||||||
|
"appId": "org.webrecorder.page.replayweb",
|
||||||
|
"artifactName": "${name}-${version}.${ext}",
|
||||||
|
"files": [
|
||||||
|
"!**/node_modules/**/*",
|
||||||
|
"dist/*.js",
|
||||||
|
"index.html",
|
||||||
|
"sw.js",
|
||||||
|
"package.json"
|
||||||
|
],
|
||||||
|
"extraResources": [
|
||||||
|
"plugins"
|
||||||
|
],
|
||||||
|
"dmg": {
|
||||||
|
"icon": "assets/icon.icns"
|
||||||
|
},
|
||||||
|
"mac": {
|
||||||
|
"category": "public.app-category.utilities",
|
||||||
|
"target": "dmg"
|
||||||
|
},
|
||||||
|
"linux": {
|
||||||
|
"category": "Archiving;Utility;",
|
||||||
|
"target": [
|
||||||
|
"AppImage"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"win": {
|
||||||
|
"target": "portable"
|
||||||
|
},
|
||||||
|
"directories": {
|
||||||
|
"buildResources": "assets",
|
||||||
|
"output": "dist"
|
||||||
|
},
|
||||||
|
"fileAssociations": [
|
||||||
|
{
|
||||||
|
"ext": "warc",
|
||||||
|
"name": "WARC File",
|
||||||
|
"description": "WARC File",
|
||||||
|
"role": "Viewer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ext": "warc.gz",
|
||||||
|
"name": "WARC File",
|
||||||
|
"description": "WARC File",
|
||||||
|
"role": "Viewer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ext": "har",
|
||||||
|
"name": "HAR File",
|
||||||
|
"description": "HAR File",
|
||||||
|
"role": "Viewer"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
*
|
||||||
|
!README
|
||||||
|
!.gitignore
|
||||||
|
|
||||||
@@ -0,0 +1,294 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const {app, session, BrowserWindow, protocol, ipcMain} = require('electron');
|
||||||
|
const path = require('path');
|
||||||
|
const fs = require('fs');
|
||||||
|
const qs = require('querystring');
|
||||||
|
|
||||||
|
const { ArchiveResponse, Rewriter } = require('wabac/src/rewrite');
|
||||||
|
|
||||||
|
const { Headers } = require('node-fetch');
|
||||||
|
|
||||||
|
global.Headers = Headers;
|
||||||
|
|
||||||
|
const { PassThrough } = require('stream');
|
||||||
|
|
||||||
|
const STATIC_PREFIX = `https://replayweb.page/`;
|
||||||
|
|
||||||
|
const REPLAY_PREFIX = STATIC_PREFIX + "wabac/";
|
||||||
|
|
||||||
|
const STATIC_PATHS = {
|
||||||
|
'index.html': 'text/html',
|
||||||
|
'dist/frontend.js': 'application/javascript',
|
||||||
|
'dist/sw.js': 'application/javascript',
|
||||||
|
'sw.js': 'application/javascript'
|
||||||
|
};
|
||||||
|
|
||||||
|
const URL_RX = /([^\/]+)\/([\d]+)(?:\w\w_)?\/(.*)$/;
|
||||||
|
|
||||||
|
const appPath = app.getAppPath();
|
||||||
|
const projPath = path.join(appPath, "../");
|
||||||
|
|
||||||
|
const pluginPath = path.join(projPath, "plugins", "PepperFlashPlayer.plugin");
|
||||||
|
|
||||||
|
console.log('app path', appPath);
|
||||||
|
console.log('proj path', projPath);
|
||||||
|
console.log('plugin path', pluginPath);
|
||||||
|
|
||||||
|
|
||||||
|
console.log('app data', app.getPath('appData'));
|
||||||
|
|
||||||
|
app.setPath('userData', path.join(app.getPath('appData'), 'replaywebpage'));
|
||||||
|
|
||||||
|
console.log('user data', app.getPath('userData'));
|
||||||
|
|
||||||
|
app.commandLine.appendSwitch('ppapi-flash-path', pluginPath);
|
||||||
|
|
||||||
|
|
||||||
|
var proxyColl;
|
||||||
|
var proxyTS;
|
||||||
|
|
||||||
|
var mainContents;
|
||||||
|
|
||||||
|
|
||||||
|
function bufferToStream(data) {
|
||||||
|
const rv = new PassThrough();
|
||||||
|
rv.push(data);
|
||||||
|
rv.push(null);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async function doIntercept(request, callback) {
|
||||||
|
console.log("request", request.url);
|
||||||
|
|
||||||
|
if (request.url.startsWith(STATIC_PREFIX)) {
|
||||||
|
let filename = request.url.slice(STATIC_PREFIX.length).split("?", 1)[0];
|
||||||
|
if (filename === "") {
|
||||||
|
filename = "index.html";
|
||||||
|
}
|
||||||
|
const type = STATIC_PATHS[filename];
|
||||||
|
|
||||||
|
if (type) {
|
||||||
|
const fullPath = path.join(__dirname, "../", filename);
|
||||||
|
|
||||||
|
console.log("fullPath: " + fullPath);
|
||||||
|
|
||||||
|
const data = fs.createReadStream(fullPath);
|
||||||
|
|
||||||
|
callback({statusCode: 200, headers: {"content-type": type}, data});
|
||||||
|
return;
|
||||||
|
|
||||||
|
} else if (request.url.startsWith(REPLAY_PREFIX)) {
|
||||||
|
const m = request.url.slice(REPLAY_PREFIX.length).match(URL_RX);
|
||||||
|
if (m) {
|
||||||
|
proxyColl = m[1];
|
||||||
|
proxyTS = m[2];
|
||||||
|
|
||||||
|
//const data = bufferToStream(`<html><head><script>window.location.href = "${m[3]}";</script></head></html>`);
|
||||||
|
//const statusCode = 200;
|
||||||
|
//const headers = {"location": m[3], "cache-control": "no-cache"};
|
||||||
|
|
||||||
|
//callback({statusCode, headers, data});
|
||||||
|
|
||||||
|
request.url = m[3];
|
||||||
|
await resolveResponse(request, callback);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return notFound(request.url, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.url.startsWith(__APP_FILE_SERVE_PREFIX__)) {
|
||||||
|
const filename = qs.unescape(request.url.slice(__APP_FILE_SERVE_PREFIX__.length));
|
||||||
|
console.log("file serve:", filename);
|
||||||
|
|
||||||
|
const stat = await fs.promises.lstat(filename);
|
||||||
|
|
||||||
|
if (!stat.isFile()) {
|
||||||
|
return notFound(filename, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
const size = stat.size;
|
||||||
|
|
||||||
|
const headers = {"Content-Length": "" + size, "Content-Type": "application/octet-stream"};
|
||||||
|
|
||||||
|
const reqHeaders = new Headers(request.headers);
|
||||||
|
|
||||||
|
const {statusCode, start, end} = parseRange(reqHeaders, headers, size);
|
||||||
|
|
||||||
|
const data = fs.createReadStream(filename, {start, end});
|
||||||
|
|
||||||
|
callback({statusCode, headers, data});
|
||||||
|
}
|
||||||
|
|
||||||
|
await resolveResponse(request, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
function notFound(url, callback) {
|
||||||
|
console.log("not found: " + url);
|
||||||
|
const data = bufferToStream(`Sorry, the url <b>${url}</b> could not be found in this archive.`);
|
||||||
|
callback({statusCode: 404, headers: {"Content-Type": 'text/html; charset="utf-8"'}, data});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function resolveResponse(request, callback) {
|
||||||
|
//mainContents.once("ipc-message", async (event, channel, status, headers, payload) => {
|
||||||
|
ipcMain.once("req:" + request.url, async (event, status, headers, payload) => {
|
||||||
|
const url = request.url;
|
||||||
|
|
||||||
|
if (status === 404 && !payload) {
|
||||||
|
return notFound(url, callback);
|
||||||
|
} else {
|
||||||
|
console.log("got response for: " + url);
|
||||||
|
}
|
||||||
|
|
||||||
|
headers = new Headers(headers);
|
||||||
|
const date = new Date();
|
||||||
|
|
||||||
|
let response = new ArchiveResponse({payload, headers, status, date, url});
|
||||||
|
|
||||||
|
const rewriter = new Rewriter({
|
||||||
|
baseUrl: url,
|
||||||
|
prefix: "",
|
||||||
|
urlRewrite: false,
|
||||||
|
contentRewrite: true,
|
||||||
|
decode: true,
|
||||||
|
useBaseRules: true
|
||||||
|
});
|
||||||
|
|
||||||
|
//const timestamp = proxyTS;
|
||||||
|
request.headers = new Headers(request.headers);
|
||||||
|
|
||||||
|
try {
|
||||||
|
response = await rewriter.rewrite(response, request);
|
||||||
|
|
||||||
|
headers = Object.fromEntries(response.headers.entries());
|
||||||
|
|
||||||
|
let data = await response.getBuffer();
|
||||||
|
|
||||||
|
if (status === 206 || status === 200) {
|
||||||
|
const {statusCode, start, end} = parseRange(request.headers, headers, data.length);
|
||||||
|
if (start !== undefined) {
|
||||||
|
data = data.slice(start, end);
|
||||||
|
}
|
||||||
|
status = statusCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = bufferToStream(data);
|
||||||
|
|
||||||
|
callback({statusCode: status, headers, data});
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
mainContents.send("getresponse", request, proxyColl, proxyTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseRange(reqHeaders, headers, size) {
|
||||||
|
let statusCode = 200;
|
||||||
|
const range = reqHeaders.get("range");
|
||||||
|
|
||||||
|
if (!range) {
|
||||||
|
return {statusCode};
|
||||||
|
}
|
||||||
|
|
||||||
|
const m = range.match(/bytes=([\d]+)-([\d]*)/);
|
||||||
|
if (!m) {
|
||||||
|
return {statusCode};
|
||||||
|
}
|
||||||
|
|
||||||
|
const start = Number(m[1]);
|
||||||
|
const end = m[2] ? Number(m[2]) : size - 1;
|
||||||
|
statusCode = 206;
|
||||||
|
if (headers) {
|
||||||
|
headers["Content-Range"] = `bytes ${start}-${end}/${size}`;
|
||||||
|
}
|
||||||
|
return {statusCode, start, end};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function createWindow () {
|
||||||
|
//createServer();
|
||||||
|
|
||||||
|
const sesh = session.defaultSession;
|
||||||
|
protocol.registerStreamProtocol("filex", async (request, callback) => {
|
||||||
|
const filename = qs.unescape(request.url.slice("filex://-".length));
|
||||||
|
console.log("filex", filename);
|
||||||
|
|
||||||
|
if (filename.endsWith("?redir")) {
|
||||||
|
const newLoc = request.url.replace("?redir", "");
|
||||||
|
console.log("redir to: " + newLoc);
|
||||||
|
callback({statusCode: 304, headers: {"Location": newLoc}, data: bufferToStream(NOT_FOUND)});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
sesh.protocol.interceptStreamProtocol("http", doIntercept);
|
||||||
|
sesh.protocol.interceptStreamProtocol("https", doIntercept);
|
||||||
|
|
||||||
|
// Create the browser window.
|
||||||
|
const mainWindow = new BrowserWindow({
|
||||||
|
width: 1024,
|
||||||
|
height: 768,
|
||||||
|
isMaximized: true,
|
||||||
|
show: false,
|
||||||
|
webPreferences: {
|
||||||
|
plugins: true,
|
||||||
|
preload: path.join(__dirname, 'preload.js'),
|
||||||
|
}
|
||||||
|
}).once('ready-to-show', () => {
|
||||||
|
|
||||||
|
//const sesh = session.fromPartition("persist:replay");
|
||||||
|
|
||||||
|
//const filter = {"urls": ['*://*/*']};
|
||||||
|
//sesh.webRequest.onBeforeRequest(filter, (request, callback) => {
|
||||||
|
// console.log("got onBeforeRequest");
|
||||||
|
// callback({cancel: false});
|
||||||
|
//});
|
||||||
|
|
||||||
|
//sesh.protocol.interceptStreamProtocol("http", doIntercept);
|
||||||
|
//sesh.protocol.interceptStreamProtocol("https", doIntercept);
|
||||||
|
|
||||||
|
mainWindow.show();
|
||||||
|
mainWindow.maximize();
|
||||||
|
});
|
||||||
|
|
||||||
|
mainWindow.loadURL(STATIC_PREFIX + "index.html");
|
||||||
|
mainWindow.webContents.openDevTools();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method will be called when Electron has finished
|
||||||
|
// initialization and is ready to create browser windows.
|
||||||
|
// Some APIs can only be used after this event occurs.
|
||||||
|
app.whenReady().then(() => {
|
||||||
|
createWindow();
|
||||||
|
|
||||||
|
app.on('activate', function () {
|
||||||
|
// On macOS it's common to re-create a window in the app when the
|
||||||
|
// dock icon is clicked and there are no other windows open.
|
||||||
|
if (BrowserWindow.getAllWindows().length === 0) createWindow()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Quit when all windows are closed.
|
||||||
|
app.on('window-all-closed', function () {
|
||||||
|
// On macOS it is common for applications and their menu bar
|
||||||
|
// to stay active until the user quits explicitly with Cmd + Q
|
||||||
|
//if (process.platform !== 'darwin')
|
||||||
|
app.quit();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
app.on('web-contents-created', (event, contents) => {
|
||||||
|
if (contents.getType() === "webview") {
|
||||||
|
contents.openDevTools();
|
||||||
|
} else if (contents.getType() === "window") {
|
||||||
|
mainContents = contents;
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
import { CollectionLoader } from 'wabac/src/loaders';
|
||||||
|
|
||||||
|
const { ipcRenderer } = require('electron');
|
||||||
|
|
||||||
|
const dbs = {};
|
||||||
|
|
||||||
|
const loader = new CollectionLoader();
|
||||||
|
|
||||||
|
async function getDB(name) {
|
||||||
|
if (!dbs[name]) {
|
||||||
|
dbs[name] = await loader.loadColl(name);
|
||||||
|
console.log(dbs[name]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dbs[name].store;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getResponse(event, request, coll, ts) {
|
||||||
|
const db = await getDB(coll);
|
||||||
|
await db.initing;
|
||||||
|
|
||||||
|
const req = {request, url: request.url, timestamp: ts};
|
||||||
|
|
||||||
|
const result = await db.getResource(req, "", {request});
|
||||||
|
|
||||||
|
const channel = "req:" + request.url;
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
ipcRenderer.send(channel, 404);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const headers = Object.fromEntries(result.headers.entries());
|
||||||
|
|
||||||
|
const buffer = await result.getBuffer();
|
||||||
|
|
||||||
|
ipcRenderer.send(channel, result.status, headers, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
ipcRenderer.on('getresponse', getResponse);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
+6
-6
@@ -268,7 +268,7 @@ class AppMain extends LitElement
|
|||||||
|
|
||||||
onStartLoad(event) {
|
onStartLoad(event) {
|
||||||
// just redirect right away?
|
// just redirect right away?
|
||||||
if (!event.detail.blobUrl) {
|
if (!event.detail.isFile) {
|
||||||
this.pageParams.set("source", event.detail.sourceUrl);
|
this.pageParams.set("source", event.detail.sourceUrl);
|
||||||
window.location.search = this.pageParams.toString();
|
window.location.search = this.pageParams.toString();
|
||||||
return;
|
return;
|
||||||
@@ -388,7 +388,7 @@ You can select a file to upload from the main page by clicking the \'Choose File
|
|||||||
|
|
||||||
source = {
|
source = {
|
||||||
sourceUrl: this.loadInfo.sourceUrl,
|
sourceUrl: this.loadInfo.sourceUrl,
|
||||||
loadUrl: this.loadInfo.blobUrl,
|
loadUrl: this.loadInfo.loadUrl,
|
||||||
name: this.loadInfo.name
|
name: this.loadInfo.name
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -537,7 +537,7 @@ class WrIndex extends LitElement
|
|||||||
}
|
}
|
||||||
|
|
||||||
async loadColls() {
|
async loadColls() {
|
||||||
const resp = await fetch("/wabac/api/index");
|
const resp = await fetch("./wabac/api/index");
|
||||||
try {
|
try {
|
||||||
const json = await resp.json();
|
const json = await resp.json();
|
||||||
this.colls = json.colls;
|
this.colls = json.colls;
|
||||||
@@ -574,7 +574,7 @@ class WrIndex extends LitElement
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.file = event.currentTarget.files[0];
|
this.file = event.currentTarget.files[0];
|
||||||
this.fileDisplayName = "file://" + this.file.name;
|
this.fileDisplayName = "file://" + (this.file.path || this.file.name);
|
||||||
this.requestUpdate();
|
this.requestUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -584,7 +584,8 @@ class WrIndex extends LitElement
|
|||||||
const detail = {sourceUrl: this.fileDisplayName};
|
const detail = {sourceUrl: this.fileDisplayName};
|
||||||
|
|
||||||
if (this.file) {
|
if (this.file) {
|
||||||
detail.blobUrl = URL.createObjectURL(this.file);
|
detail.isFile = true;
|
||||||
|
detail.loadUrl = this.file.path ? __APP_FILE_SERVE_PREFIX__ + this.file.path : URL.createObjectURL(this.file);
|
||||||
detail.name = this.fileDisplayName;
|
detail.name = this.fileDisplayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2082,7 +2083,6 @@ function registerSW(url) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
//const swPromise = initSW(__SW_PATH__ + "?replayPrefix=wabac&stats=true");
|
|
||||||
const swPromise = registerSW(__SW_PATH__);
|
const swPromise = registerSW(__SW_PATH__);
|
||||||
await swPromise;
|
await swPromise;
|
||||||
customElements.define("app-main", AppMain);
|
customElements.define("app-main", AppMain);
|
||||||
|
|||||||
+62
-4
@@ -2,8 +2,59 @@ const path = require('path');
|
|||||||
const webpack = require('webpack');
|
const webpack = require('webpack');
|
||||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||||
|
|
||||||
module.exports = (env, argv) => {
|
// fake url used in app to serve files
|
||||||
|
// can not use custom scheme due to service worker issues
|
||||||
|
const APP_FILE_SERVE_PREFIX = "https://files.replayweb.page";
|
||||||
|
|
||||||
|
|
||||||
|
// if running in dev-server, build sw.js in dist/sw.js
|
||||||
|
// to support hot-reloading, otherwise, build sw.js in root
|
||||||
|
const IS_DEV_SERVER = process.env.WEBPACK_DEV_SERVER;
|
||||||
|
|
||||||
|
|
||||||
|
const electronMainConfig = (env, argv) => {
|
||||||
return {
|
return {
|
||||||
|
target: 'electron-main',
|
||||||
|
mode: 'production',
|
||||||
|
entry: {
|
||||||
|
'electron': './src/electron-main.js',
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
path: path.join(__dirname, 'dist'),
|
||||||
|
filename: '[name].js'
|
||||||
|
},
|
||||||
|
node: {
|
||||||
|
__dirname: false,
|
||||||
|
__filename: false,
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new webpack.DefinePlugin({
|
||||||
|
__APP_FILE_SERVE_PREFIX__ : JSON.stringify(APP_FILE_SERVE_PREFIX),
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const electronPreloadConfig = (env, argv) => {
|
||||||
|
return {
|
||||||
|
target: 'electron-preload',
|
||||||
|
mode: 'production',
|
||||||
|
entry: {
|
||||||
|
'preload': './src/electron-preload.js',
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
path: path.join(__dirname, 'dist'),
|
||||||
|
filename: '[name].js'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const browserConfig = (env, argv) => {
|
||||||
|
return {
|
||||||
|
target: 'web',
|
||||||
mode: 'production',
|
mode: 'production',
|
||||||
entry: {
|
entry: {
|
||||||
'frontend': './src/frontend.js',
|
'frontend': './src/frontend.js',
|
||||||
@@ -16,7 +67,7 @@ module.exports = (env, argv) => {
|
|||||||
filename: (chunkData) => {
|
filename: (chunkData) => {
|
||||||
switch (chunkData.chunk.name) {
|
switch (chunkData.chunk.name) {
|
||||||
case 'sw':
|
case 'sw':
|
||||||
return argv.mode === 'production' ? '../[name].js': '[name].js';
|
return !IS_DEV_SERVER ? '../[name].js': '[name].js';
|
||||||
|
|
||||||
case 'wombat':
|
case 'wombat':
|
||||||
return '../static/[name].js';
|
return '../static/[name].js';
|
||||||
@@ -31,6 +82,10 @@ module.exports = (env, argv) => {
|
|||||||
publicPath: '/dist/'
|
publicPath: '/dist/'
|
||||||
},
|
},
|
||||||
|
|
||||||
|
externals: {
|
||||||
|
electron: 'electron',
|
||||||
|
},
|
||||||
|
|
||||||
devServer: {
|
devServer: {
|
||||||
compress: true,
|
compress: true,
|
||||||
port: 9990,
|
port: 9990,
|
||||||
@@ -42,7 +97,8 @@ module.exports = (env, argv) => {
|
|||||||
plugins: [
|
plugins: [
|
||||||
new MiniCssExtractPlugin(),
|
new MiniCssExtractPlugin(),
|
||||||
new webpack.DefinePlugin({
|
new webpack.DefinePlugin({
|
||||||
__SW_PATH__: JSON.stringify(argv.mode === 'production' ? './sw.js' : './dist/sw.js')
|
__SW_PATH__: JSON.stringify(!IS_DEV_SERVER ? './sw.js' : './dist/sw.js'),
|
||||||
|
__APP_FILE_SERVE_PREFIX__ : JSON.stringify(APP_FILE_SERVE_PREFIX),
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|
||||||
@@ -67,6 +123,8 @@ module.exports = (env, argv) => {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
module.exports = [ browserConfig, electronMainConfig, electronPreloadConfig ];
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user