serviceWorker.js 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. import Keychain from './keychain';
  2. import { downloadStream } from './api';
  3. import TransformStream from './transformStream';
  4. let noSave = false;
  5. const map = new Map();
  6. self.addEventListener('install', event => {
  7. self.skipWaiting();
  8. });
  9. self.addEventListener('activate', event => {
  10. self.clients.claim();
  11. });
  12. async function decryptStream(request) {
  13. const id = request.url.split('/')[5];
  14. try {
  15. const file = map.get(id);
  16. file.download = downloadStream(id, file.keychain);
  17. const stream = await file.download.result;
  18. // eslint-disable-next-line no-undef
  19. const progStream = new TransformStream({
  20. transform: (chunk, controller) => {
  21. file.progress += chunk.length;
  22. controller.enqueue(chunk);
  23. }
  24. });
  25. const readStream = stream.pipeThrough(progStream);
  26. const decrypted = file.keychain.decryptStream(readStream);
  27. const headers = {
  28. 'Content-Disposition': 'attachment; filename=' + file.filename,
  29. 'Content-Type': file.type,
  30. 'Content-Length': file.size
  31. };
  32. const body = decrypted.local ? decrypted.nativeReadable : decrypted;
  33. return new Response(body, { headers });
  34. } catch (e) {
  35. if (noSave) {
  36. return new Response(null, { status: e.message });
  37. }
  38. const redirectRes = await fetch(`/download/${id}`);
  39. return new Response(redirectRes.body, { status: 302 });
  40. }
  41. }
  42. self.onfetch = event => {
  43. const req = event.request.clone();
  44. if (req.url.includes('/api/download')) {
  45. event.respondWith(decryptStream(req));
  46. }
  47. };
  48. self.onmessage = event => {
  49. if (event.data.request === 'init') {
  50. noSave = event.data.noSave;
  51. const info = {
  52. keychain: new Keychain(event.data.key),
  53. filename: event.data.filename,
  54. type: event.data.type,
  55. size: event.data.size,
  56. progress: 0,
  57. cancelled: false
  58. };
  59. if (event.data.requiresPassword) {
  60. info.keychain.setPassword(event.data.password, event.data.url);
  61. }
  62. map.set(event.data.id, info);
  63. event.ports[0].postMessage('file info received');
  64. } else if (event.data.request === 'progress') {
  65. const file = map.get(event.data.id);
  66. if (file.cancelled) {
  67. event.ports[0].postMessage({ error: 'cancelled' });
  68. } else {
  69. event.ports[0].postMessage({ progress: file.progress });
  70. }
  71. } else if (event.data.request === 'cancel') {
  72. const file = map.get(event.data.id);
  73. file.cancelled = true;
  74. if (file.download) {
  75. file.download.cancel();
  76. }
  77. event.ports[0].postMessage('download cancelled');
  78. }
  79. };