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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "replaywebpage",
3
3
  "productName": "ReplayWeb.page",
4
- "version": "1.7.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.14",
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": "^4.3.9",
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": "^21.3.1",
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
- "webpack": "^5.74.0",
46
- "webpack-cli": "^4.8.0",
47
- "webpack-dev-server": "^4.2.1"
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: url.href,
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
- const url = new URL(__APP_FILE_SERVE_PREFIX__);
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.ipfsClient = null;
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(includePlugins = false) {
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
- const method = request.method;
325
- const response = await fetch(request.url, {method, headers});
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 };