mirror of
https://github.com/TeamPiped/Piped.git
synced 2024-08-30 18:43:17 +00:00
Add support to sync preferences.
This commit is contained in:
parent
2d7801eb67
commit
ee20e0ccdf
54
src/App.vue
54
src/App.vue
@ -19,6 +19,10 @@ import FooterComponent from "./components/FooterComponent.vue";
|
|||||||
|
|
||||||
const darkModePreference = window.matchMedia("(prefers-color-scheme: dark)");
|
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 {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
NavBar,
|
NavBar,
|
||||||
@ -39,6 +43,7 @@ export default {
|
|||||||
this.fetchJson(this.authApiUrl() + "/config")
|
this.fetchJson(this.authApiUrl() + "/config")
|
||||||
.then(config => {
|
.then(config => {
|
||||||
this.config = config;
|
this.config = config;
|
||||||
|
state.config = config;
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.onConfigLoaded();
|
this.onConfigLoaded();
|
||||||
@ -120,6 +125,55 @@ export default {
|
|||||||
const root = document.querySelector(":root");
|
const root = document.querySelector(":root");
|
||||||
this.theme == "dark" ? root.classList.add("dark") : root.classList.remove("dark");
|
this.theme == "dark" ? root.classList.add("dark") : root.classList.remove("dark");
|
||||||
},
|
},
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -29,15 +29,12 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
<<<<<<< Updated upstream
|
|
||||||
=======
|
|
||||||
props: {
|
props: {
|
||||||
config: {
|
config: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
>>>>>>> Stashed changes
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
donationHref: null,
|
donationHref: null,
|
||||||
@ -45,11 +42,6 @@ export default {
|
|||||||
privacyPolicyHref: null,
|
privacyPolicyHref: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
props: {
|
|
||||||
config: {
|
|
||||||
type: Object,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
watch: {
|
watch: {
|
||||||
config: {
|
config: {
|
||||||
handler() {
|
handler() {
|
||||||
|
@ -379,6 +379,9 @@
|
|||||||
<script>
|
<script>
|
||||||
import CountryMap from "@/utils/CountryMaps/en.json";
|
import CountryMap from "@/utils/CountryMaps/en.json";
|
||||||
import ConfirmModal from "./ConfirmModal.vue";
|
import ConfirmModal from "./ConfirmModal.vue";
|
||||||
|
import { state } from "../utils/store";
|
||||||
|
import { encryptAESGCM, decodeBase64ToArray } from "../utils/encryptionUtils";
|
||||||
|
import { compressGzip } from "../utils/compressionUtils";
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
ConfirmModal,
|
ConfirmModal,
|
||||||
@ -612,6 +615,53 @@ export default {
|
|||||||
localStorage.setItem("hideWatched", this.hideWatched);
|
localStorage.setItem("hideWatched", this.hideWatched);
|
||||||
localStorage.setItem("mobileChapterLayout", this.mobileChapterLayout);
|
localStorage.setItem("mobileChapterLayout", this.mobileChapterLayout);
|
||||||
|
|
||||||
|
const config = state.config;
|
||||||
|
|
||||||
|
const key = this.getPreferenceString("e2ee_key", null, false);
|
||||||
|
|
||||||
|
if (config.s3Enabled && this.authenticated && key) {
|
||||||
|
const statResult = await this.fetchJson(
|
||||||
|
this.authApiUrl() + "/storage/stat",
|
||||||
|
{
|
||||||
|
file: "pipedpref",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: this.getAuthToken(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const etag = statResult.etag;
|
||||||
|
|
||||||
|
// export localStorage to JSON
|
||||||
|
const localStorageJson = {};
|
||||||
|
for (let i = 0; i < localStorage.length; i++) {
|
||||||
|
const key = localStorage.key(i);
|
||||||
|
localStorageJson[key] = localStorage.getItem(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
const importedKey = decodeBase64ToArray(key).buffer;
|
||||||
|
|
||||||
|
const data = await compressGzip(JSON.stringify(localStorageJson));
|
||||||
|
|
||||||
|
const encrypted = await encryptAESGCM(data, importedKey);
|
||||||
|
|
||||||
|
await this.fetchJson(
|
||||||
|
this.authApiUrl() + "/storage/put",
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
Authorization: this.getAuthToken(),
|
||||||
|
"x-file-name": "pipedpref",
|
||||||
|
"x-last-etag": etag,
|
||||||
|
},
|
||||||
|
body: encrypted,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (shouldReload) window.location.reload();
|
if (shouldReload) window.location.reload();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
3
src/utils/store.js
Normal file
3
src/utils/store.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { reactive } from "vue";
|
||||||
|
|
||||||
|
export const state = reactive({});
|
Loading…
Reference in New Issue
Block a user