replaywebpage 1.7.7 → 1.7.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +9 -8
- package/src/appmain.js +1 -4
- package/src/chooser.js +1 -3
- package/src/electron-replay-app.js +85 -102
- package/sw.js +11 -11
- package/ui.js +3 -3
- package/src/electron-ipfs-handler.js +0 -44
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "replaywebpage",
|
|
3
3
|
"productName": "ReplayWeb.page",
|
|
4
|
-
"version": "1.7.
|
|
4
|
+
"version": "1.7.8",
|
|
5
5
|
"description": "Serverless Web Archive Replay",
|
|
6
6
|
"repository": "https://github.com/webrecorder/replayweb.page",
|
|
7
7
|
"homepage": "https://replayweb.page/",
|
|
@@ -9,11 +9,10 @@
|
|
|
9
9
|
"license": "AGPL-3.0-or-later",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@fortawesome/fontawesome-free": "^5.15.4",
|
|
12
|
-
"@webrecorder/wabac": "^2.13.
|
|
13
|
-
"auto-js-ipfs": "^1.5.1",
|
|
12
|
+
"@webrecorder/wabac": "^2.13.15",
|
|
14
13
|
"bulma": "^0.9.3",
|
|
15
14
|
"electron-log": "^4.4.1",
|
|
16
|
-
"electron-updater": "^
|
|
15
|
+
"electron-updater": "^5.3.0",
|
|
17
16
|
"fetch-ndjson": "^1.1.6",
|
|
18
17
|
"flexsearch": "^0.7.21",
|
|
19
18
|
"keyword-mark-element": "^0.2.0",
|
|
@@ -21,6 +20,7 @@
|
|
|
21
20
|
"marked": "^4.0.10",
|
|
22
21
|
"mime-types": "^2.1.32",
|
|
23
22
|
"minimist": "^1.2.5",
|
|
23
|
+
"node-fetch": "^3.3.0",
|
|
24
24
|
"pretty-bytes": "^5.6.0",
|
|
25
25
|
"process": "^0.11.10",
|
|
26
26
|
"querystring-es3": "^0.2.1",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"copy-webpack-plugin": "^9.0.1",
|
|
33
33
|
"css-loader": "^6.2.0",
|
|
34
|
-
"electron": "^
|
|
34
|
+
"electron": "^22.0.0",
|
|
35
35
|
"electron-builder": "^23.0.3",
|
|
36
36
|
"electron-notarize": "^1.1.1",
|
|
37
37
|
"eslint": "^8.23.1",
|
|
@@ -42,9 +42,10 @@
|
|
|
42
42
|
"sass-loader": "^12.1.0",
|
|
43
43
|
"style-loader": "^3.2.1",
|
|
44
44
|
"svg-inline-loader": "^0.8.2",
|
|
45
|
-
"
|
|
46
|
-
"webpack
|
|
47
|
-
"webpack-
|
|
45
|
+
"url": "^0.11.0",
|
|
46
|
+
"webpack": "^5.75.0",
|
|
47
|
+
"webpack-cli": "^5.0.1",
|
|
48
|
+
"webpack-dev-server": "^4.11.1"
|
|
48
49
|
},
|
|
49
50
|
"files": [
|
|
50
51
|
"/assets",
|
package/src/appmain.js
CHANGED
|
@@ -401,12 +401,9 @@ class ReplayWebApp extends LitElement
|
|
|
401
401
|
}
|
|
402
402
|
|
|
403
403
|
if (IS_APP && this.sourceUrl.startsWith("file://")) {
|
|
404
|
-
// eslint-disable-next-line no-undef
|
|
405
|
-
const url = new URL(__APP_FILE_SERVE_PREFIX__);
|
|
406
|
-
url.searchParams.set("filename", this.sourceUrl.slice("file://".length));
|
|
407
404
|
this.loadInfo = {
|
|
408
405
|
sourceUrl: this.sourceUrl,
|
|
409
|
-
loadUrl:
|
|
406
|
+
loadUrl: this.sourceUrl.replace("file://", "file2://")
|
|
410
407
|
};
|
|
411
408
|
}
|
|
412
409
|
}
|
package/src/chooser.js
CHANGED
|
@@ -79,9 +79,7 @@ class Chooser extends LitElement
|
|
|
79
79
|
// file.path only available in electron app
|
|
80
80
|
if (this.file.path) {
|
|
81
81
|
// eslint-disable-next-line no-undef
|
|
82
|
-
|
|
83
|
-
url.searchParams.set("filename", this.file.path);
|
|
84
|
-
loadInfo.loadUrl = url.href;
|
|
82
|
+
loadInfo.loadUrl = "file2://" + this.file.path;
|
|
85
83
|
loadInfo.noCache = true;
|
|
86
84
|
} else if (this.fileHandle) {
|
|
87
85
|
loadInfo.loadUrl = this.fileDisplayName;
|
|
@@ -3,13 +3,12 @@
|
|
|
3
3
|
import fetch from "node-fetch";
|
|
4
4
|
import { Headers } from "node-fetch";
|
|
5
5
|
|
|
6
|
-
import {app, session, BrowserWindow, ipcMain, screen, shell } from "electron";
|
|
6
|
+
import {app, session, BrowserWindow, ipcMain, protocol, screen, shell } from "electron";
|
|
7
7
|
|
|
8
8
|
import path from "path";
|
|
9
9
|
import fs from "fs";
|
|
10
10
|
|
|
11
11
|
import { ArchiveResponse, Rewriter } from "@webrecorder/wabac/src/rewrite";
|
|
12
|
-
import {create as createIPFS} from "auto-js-ipfs";
|
|
13
12
|
|
|
14
13
|
import { PassThrough, Readable } from "stream";
|
|
15
14
|
|
|
@@ -17,6 +16,7 @@ import { autoUpdater } from "electron-updater";
|
|
|
17
16
|
import log from "electron-log";
|
|
18
17
|
|
|
19
18
|
import mime from "mime-types";
|
|
19
|
+
import url from "url";
|
|
20
20
|
|
|
21
21
|
global.Headers = Headers;
|
|
22
22
|
global.fetch = fetch;
|
|
@@ -25,6 +25,8 @@ const STATIC_PREFIX = "http://localhost:5471/";
|
|
|
25
25
|
|
|
26
26
|
const REPLAY_PREFIX = STATIC_PREFIX + "w/";
|
|
27
27
|
|
|
28
|
+
const FILE_PROTO = "file2";
|
|
29
|
+
|
|
28
30
|
const URL_RX = /([^/]+)\/([\d]+)(?:\w\w_)?\/(.*)$/;
|
|
29
31
|
|
|
30
32
|
// ============================================================================
|
|
@@ -50,7 +52,7 @@ class ElectronReplayApp
|
|
|
50
52
|
|
|
51
53
|
this.screenSize = {width: 1024, height: 768};
|
|
52
54
|
|
|
53
|
-
this.
|
|
55
|
+
this.origUA = null;
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
get mainWindowWebPreferences() {
|
|
@@ -69,7 +71,7 @@ class ElectronReplayApp
|
|
|
69
71
|
return "index.html";
|
|
70
72
|
}
|
|
71
73
|
|
|
72
|
-
init(
|
|
74
|
+
init() {
|
|
73
75
|
// Single instance check
|
|
74
76
|
const gotTheLock = app.requestSingleInstanceLock();
|
|
75
77
|
|
|
@@ -83,36 +85,10 @@ class ElectronReplayApp
|
|
|
83
85
|
});
|
|
84
86
|
}
|
|
85
87
|
|
|
86
|
-
if (includePlugins) {
|
|
87
|
-
switch (process.platform) {
|
|
88
|
-
case "win32":
|
|
89
|
-
this.pluginPath = path.join(this.projPath, "plugins-win", `pepflashplayer-x86${process.arch === "x64" ? "_64" : ""}.dll`);
|
|
90
|
-
break;
|
|
91
|
-
|
|
92
|
-
case "darwin":
|
|
93
|
-
this.pluginPath = path.join(this.projPath, "plugins-mac", "PepperFlashPlayer.plugin");
|
|
94
|
-
break;
|
|
95
|
-
|
|
96
|
-
case "linux":
|
|
97
|
-
this.pluginPath = path.join(this.projPath, "plugins-mac", "libpepflashplayer.so");
|
|
98
|
-
break;
|
|
99
|
-
|
|
100
|
-
default:
|
|
101
|
-
console.log("platform unsupported: " + process.platform);
|
|
102
|
-
break;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
app.commandLine.appendSwitch("ppapi-flash-path", this.pluginPath);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
88
|
console.log("app path", this.appPath);
|
|
109
89
|
console.log("dir name", __dirname);
|
|
110
90
|
console.log("proj path", this.projPath);
|
|
111
91
|
|
|
112
|
-
if (includePlugins) {
|
|
113
|
-
console.log("plugin path", this.pluginPath);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
92
|
console.log("app data", app.getPath("appData"));
|
|
117
93
|
console.log("user data", app.getPath("userData"));
|
|
118
94
|
|
|
@@ -120,6 +96,19 @@ class ElectronReplayApp
|
|
|
120
96
|
app.setPath("userData", path.join(app.getPath("appData"), this.profileName));
|
|
121
97
|
}
|
|
122
98
|
|
|
99
|
+
protocol.registerSchemesAsPrivileged([{
|
|
100
|
+
scheme: FILE_PROTO,
|
|
101
|
+
privileges: {
|
|
102
|
+
standard: false,
|
|
103
|
+
secure: true,
|
|
104
|
+
bypassCSP: true,
|
|
105
|
+
allowServiceWorkers: true,
|
|
106
|
+
supportFetchAPI: true,
|
|
107
|
+
corsEnabled: true,
|
|
108
|
+
stream: true
|
|
109
|
+
}
|
|
110
|
+
}]);
|
|
111
|
+
|
|
123
112
|
app.on("will-finish-launching", () => {
|
|
124
113
|
app.on("open-file", (event, filePath) => {
|
|
125
114
|
this.openNextFile = filePath;
|
|
@@ -155,9 +144,6 @@ class ElectronReplayApp
|
|
|
155
144
|
}
|
|
156
145
|
|
|
157
146
|
onAppReady() {
|
|
158
|
-
const ipfsRepoPath = path.join(app.getPath("userData"), "js-ipfs");
|
|
159
|
-
console.log("ipfs path", ipfsRepoPath);
|
|
160
|
-
|
|
161
147
|
this.checkUpdates();
|
|
162
148
|
|
|
163
149
|
this.screenSize = screen.getPrimaryDisplay().workAreaSize;
|
|
@@ -184,9 +170,48 @@ class ElectronReplayApp
|
|
|
184
170
|
|
|
185
171
|
sesh.protocol.interceptStreamProtocol("http", (request, callback) => this.doIntercept(request, callback));
|
|
186
172
|
|
|
173
|
+
protocol.registerStreamProtocol(FILE_PROTO, (request, callback) => this.doHandleFile(request, callback));
|
|
174
|
+
|
|
175
|
+
this.origUA = sesh.getUserAgent();
|
|
176
|
+
|
|
187
177
|
this.mainWindow = this.createMainWindow(process.argv);
|
|
188
178
|
}
|
|
189
179
|
|
|
180
|
+
async doHandleFile(request, callback) {
|
|
181
|
+
//const parsedUrl = new URL(request.url);
|
|
182
|
+
//const filename = parsedUrl.searchParams.get("filename");
|
|
183
|
+
|
|
184
|
+
if (request.url === FILE_PROTO + "://localhost") {
|
|
185
|
+
callback({statusCode: 200, data: null});
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const filename = url.fileURLToPath(request.url.replace(FILE_PROTO, "file"));
|
|
190
|
+
|
|
191
|
+
const headers = {"Content-Type": "application/octet-stream"};
|
|
192
|
+
const reqHeaders = new Headers(request.headers);
|
|
193
|
+
|
|
194
|
+
if (filename) {
|
|
195
|
+
const stat = await fs.promises.lstat(filename);
|
|
196
|
+
|
|
197
|
+
if (!stat.isFile()) {
|
|
198
|
+
return this.notFound(filename, callback);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const size = stat.size;
|
|
202
|
+
|
|
203
|
+
const {statusCode, start, end} = this.parseRange(reqHeaders, headers, size);
|
|
204
|
+
|
|
205
|
+
const data = request.method === "HEAD" ? null : fs.createReadStream(filename, {start, end});
|
|
206
|
+
|
|
207
|
+
callback({statusCode, headers, data});
|
|
208
|
+
return;
|
|
209
|
+
|
|
210
|
+
} else {
|
|
211
|
+
return this.notFound("No Resource Specified", callback);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
190
215
|
_bufferToStream(data) {
|
|
191
216
|
const rv = new PassThrough();
|
|
192
217
|
rv.push(data);
|
|
@@ -243,73 +268,6 @@ class ElectronReplayApp
|
|
|
243
268
|
return this.notFound(request.url, callback);
|
|
244
269
|
}
|
|
245
270
|
|
|
246
|
-
// eslint-disable-next-line no-undef
|
|
247
|
-
if (request.url.startsWith(__APP_FILE_SERVE_PREFIX__)) {
|
|
248
|
-
const parsedUrl = new URL(request.url);
|
|
249
|
-
|
|
250
|
-
const filename = parsedUrl.searchParams.get("filename");
|
|
251
|
-
const ipfsCID = parsedUrl.searchParams.get("ipfs");
|
|
252
|
-
|
|
253
|
-
const headers = {"Content-Type": "application/octet-stream"};
|
|
254
|
-
const reqHeaders = new Headers(request.headers);
|
|
255
|
-
|
|
256
|
-
if (filename) {
|
|
257
|
-
console.log("file serve:", filename);
|
|
258
|
-
|
|
259
|
-
const stat = await fs.promises.lstat(filename);
|
|
260
|
-
|
|
261
|
-
if (!stat.isFile()) {
|
|
262
|
-
return this.notFound(filename, callback);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
const size = stat.size;
|
|
266
|
-
|
|
267
|
-
const {statusCode, start, end} = this.parseRange(reqHeaders, headers, size);
|
|
268
|
-
|
|
269
|
-
const data = request.method === "HEAD" ? null : fs.createReadStream(filename, {start, end});
|
|
270
|
-
|
|
271
|
-
callback({statusCode, headers, data});
|
|
272
|
-
return;
|
|
273
|
-
|
|
274
|
-
} else if (ipfsCID) {
|
|
275
|
-
const ipfsURL = `ipfs://${ipfsCID}}`;
|
|
276
|
-
|
|
277
|
-
console.log("ipfs serve:", ipfsCID);
|
|
278
|
-
|
|
279
|
-
// TODO: Pass in config?
|
|
280
|
-
this.ipfsClient = await createIPFS();
|
|
281
|
-
|
|
282
|
-
console.log("inited");
|
|
283
|
-
|
|
284
|
-
let size = await this.ipfsClient.getSize(ipfsURL);
|
|
285
|
-
|
|
286
|
-
console.log("got size", size);
|
|
287
|
-
|
|
288
|
-
if (size === null) {
|
|
289
|
-
return this.notFound(ipfsCID, callback);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
const {statusCode, start, end} = this.parseRange(reqHeaders, headers, size);
|
|
293
|
-
|
|
294
|
-
let data = null;
|
|
295
|
-
|
|
296
|
-
if (request.method === "GET") {
|
|
297
|
-
const offset = start || 0;
|
|
298
|
-
const length = end ? end - start + 1 : size;
|
|
299
|
-
data = Readable.from(await this.ipfsClient.get(ipfsURL, {
|
|
300
|
-
start: offset,
|
|
301
|
-
end: offset +length
|
|
302
|
-
}));
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
callback({statusCode, headers, data});
|
|
306
|
-
return;
|
|
307
|
-
|
|
308
|
-
} else {
|
|
309
|
-
return this.notFound("No Resource Specified", callback);
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
|
|
313
271
|
// possible 'live leak' attempt, return archived version, if any
|
|
314
272
|
if (request.referrer && request.referrer.startsWith(REPLAY_PREFIX)) {
|
|
315
273
|
return await this.resolveArchiveResponse(request, callback);
|
|
@@ -320,9 +278,24 @@ class ElectronReplayApp
|
|
|
320
278
|
|
|
321
279
|
async proxyLive(request, callback) {
|
|
322
280
|
let headers = request.headers;
|
|
281
|
+
const {method, url, uploadData} = request;
|
|
282
|
+
|
|
283
|
+
const body = uploadData ? Readable.from(readBody(uploadData, session.defaultSession)) : null;
|
|
323
284
|
|
|
324
|
-
|
|
325
|
-
|
|
285
|
+
if (this.origUA) {
|
|
286
|
+
// pass UA if origUA is set
|
|
287
|
+
headers["User-Agent"] = this.origUA;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
let response;
|
|
291
|
+
|
|
292
|
+
try {
|
|
293
|
+
response = await fetch(url, {method, headers, body});
|
|
294
|
+
} catch (e) {
|
|
295
|
+
console.warn("fetch failed for: " + url);
|
|
296
|
+
callback({statusCode: 502, headers: {}, data: null});
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
326
299
|
const data = method === "HEAD" ? null : response.body;
|
|
327
300
|
const statusCode = response.status;
|
|
328
301
|
|
|
@@ -478,4 +451,14 @@ class ElectronReplayApp
|
|
|
478
451
|
}
|
|
479
452
|
}
|
|
480
453
|
|
|
454
|
+
async function * readBody (body, session) {
|
|
455
|
+
for (const chunk of body) {
|
|
456
|
+
if (chunk.bytes) {
|
|
457
|
+
yield await Promise.resolve(chunk.bytes);
|
|
458
|
+
} else if (chunk.blobUUID) {
|
|
459
|
+
yield await session.getBlobData(chunk.blobUUID);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
481
464
|
export { ElectronReplayApp, STATIC_PREFIX };
|