diff --git a/package.json b/package.json index 54e2f8b9..1af179b2 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "linkify-html": "4.1.1", "linkifyjs": "4.1.1", "mux.js": "6.3.0", + "pako": "2.1.0", "qrcode": "^1.5.3", "shaka-player": "4.5.0", "stream-browserify": "3.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 16a78d56..7c30ce84 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -38,6 +38,9 @@ dependencies: mux.js: specifier: 6.3.0 version: 6.3.0 + pako: + specifier: 2.1.0 + version: 2.1.0 qrcode: specifier: ^1.5.3 version: 1.5.3 @@ -4194,6 +4197,10 @@ packages: engines: {node: '>=6'} dev: false + /pako@2.1.0: + resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==} + dev: false + /parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} diff --git a/src/App.vue b/src/App.vue index 735ced1b..2104c306 100644 --- a/src/App.vue +++ b/src/App.vue @@ -9,7 +9,7 @@ - + @@ -19,6 +19,10 @@ import FooterComponent from "./components/FooterComponent.vue"; const darkModePreference = window.matchMedia("(prefers-color-scheme: dark)"); +import { generateKey, encodeArrayToBase64, decodeBase64ToArray, decryptAESGCM } from "./utils/encryptionUtils"; +import { decompressGzip } from "./utils/compressionUtils"; +import { state } from "./utils/store"; + export default { components: { NavBar, @@ -27,6 +31,7 @@ export default { data() { return { theme: "dark", + config: null, }; }, mounted() { @@ -35,6 +40,15 @@ export default { this.setTheme(); }); + this.fetchJson(this.authApiUrl() + "/config") + .then(config => { + this.config = config; + state.config = config; + }) + .then(() => { + this.onConfigLoaded(); + }); + if ("indexedDB" in window) { const request = indexedDB.open("piped-db", 5); request.onupgradeneeded = ev => { @@ -115,6 +129,55 @@ export default { const themeColor = document.querySelector("meta[name='theme-color']"); themeColor.setAttribute("content", currentColor[this.theme]); }, + async onConfigLoaded() { + if (this.config.s3Enabled && this.authenticated) { + if (this.getPreferenceBoolean("syncPreferences", false, false)) { + var e2e2_b64_key = this.getPreferenceString("e2ee_key", null, false); + if (!e2e2_b64_key) { + const key = new Uint8Array(await generateKey()); + const encoded = encodeArrayToBase64(key); + this.setPreference("e2ee_key", encoded); + e2e2_b64_key = encoded; + } + + const statResult = await this.fetchJson( + this.authApiUrl() + "/storage/stat", + { + file: "pipedpref", + }, + { + headers: { + Authorization: this.getAuthToken(), + }, + }, + ); + + if (statResult.status === "exists") { + const data = await fetch(this.authApiUrl() + "/storage/get?file=pipedpref", { + method: "GET", + headers: { + Authorization: this.getAuthToken(), + }, + }).then(resp => resp.arrayBuffer()); + + const cryptoKey = decodeBase64ToArray(e2e2_b64_key).buffer; + + const decrypted = await decryptAESGCM(data, cryptoKey); + + const decompressed = await decompressGzip(new Uint8Array(decrypted)); + + const localStorageJson = JSON.parse(decompressed); + + // import into localStorage + for (var key in localStorageJson) { + if (Object.prototype.hasOwnProperty.call(localStorageJson, key)) { + localStorage[key] = localStorageJson[key]; + } + } + } + } + } + }, }, }; diff --git a/src/components/FooterComponent.vue b/src/components/FooterComponent.vue index 38212347..f0441678 100644 --- a/src/components/FooterComponent.vue +++ b/src/components/FooterComponent.vue @@ -29,6 +29,12 @@