This repository has been archived on 2024-10-24. You can view files and clone it, but cannot push or open issues or pull requests.
burgernotes-web/static/js/migrate.js

190 lines
7.1 KiB
JavaScript

// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0
let secretKey = localStorage.getItem("PRIVATE-secretKey")
let cryptoKey = localStorage.getItem("PRIVATE-cryptoKey")
if (secretKey === null || cryptoKey === null) {
window.location.replace("/login")
document.body.innerHTML = "Redirecting..."
throw new Error();
}
let remote = localStorage.getItem("SETTING-homeServer")
if (remote == null) {
localStorage.setItem("SETTING-homeServer", "https://notes.ailur.dev")
remote = "https://notes.ailur.dev"
}
let notesPlainText = ""
let information
let backButton
let titleBox
let inputTypeGlobal = 0
document.addEventListener("DOMContentLoaded", function() {
information = document.getElementById("information")
backButton = document.getElementById("backButton")
titleBox = document.getElementById("title")
})
function showInput(inputType) {
inputTypeGlobal = inputType
switch (inputType) {
case 0:
information.innerText = "Welcome to the Burgernotes Migration wizard! Before we begin migration, there are a few things you should know."
titleBox.innerText = "Burgernotes Migrator"
backButton.classList.add("hidden")
break
case 1:
information.innerText = "This migration process may corrupt your data if interrupted. Please ensure you have a stable internet connection. Press continue to download a backup of your notes, just in case."
titleBox.innerText = "Disclaimer"
backButton.classList.remove("hidden")
break
case 2:
information.innerText = "Now that you have a backup of your notes, you can proceed to the next step. Press continue to begin migration."
titleBox.innerText = "Backup Complete"
break
case 3:
information.innerText = "You have successfully migrated to new Burgernotes! Enjoy the more secure and feature-rich experience. Click continue to return to the app."
titleBox.innerText = "Migration Complete"
break
}
}
function buttonClick() {
switch (inputTypeGlobal) {
case 0:
showInput(1)
break
case 1:
exportNotes().then((data) => {
if (data !== undefined) {
notesPlainText = data
let blob = new Blob([JSON.stringify(data)], {type: "application/json"})
let url = URL.createObjectURL(blob)
let a = document.createElement("a")
a.href = url
a.download = "backup.json"
a.click()
showInput(2)
}
})
break
case 2:
importNotes(notesPlainText).then((status) => {
if (status === 200) {
showInput(3)
} else if (status === -1 || status === -2) {
titleBox.innerText = "Error!"
information.innerText = "An error occurred while migrating your notes. Please try again by pressing continue."
console.log(status)
} else {
titleBox.innerText = "Critical Error!"
information.innerText = "All your notes have been lost. Good thing you have a backup! Press continue to return to the app, so you can import your backup."
inputTypeGlobal = 3
}
})
break
case 3:
window.location.href = "/app"
break
}
}
function back() {
showInput(inputTypeGlobal - 1)
}
async function getKey() {
let salt = new TextEncoder().encode("I love Burgernotes!")
let cryptoKeyFormatted = await window.crypto.subtle.importKey("raw", new TextEncoder().encode(cryptoKey), "PBKDF2", false, ["deriveBits", "deriveKey"])
return await window.crypto.subtle.deriveKey({
name: "PBKDF2",
salt,
iterations: 1,
hash: "SHA-512"
}, cryptoKeyFormatted, {name: "AES-GCM", length: 256}, true, ["encrypt", "decrypt"])
}
function arrayBufferToBase64(buffer) {
const uint8Array = new Uint8Array(buffer);
return btoa(String.fromCharCode.apply(null, uint8Array))
}
async function encrypt(text) {
let cryptoKey = await getKey()
let iv = window.crypto.getRandomValues(new Uint8Array(12))
let encrypted = await window.crypto.subtle.encrypt({
name: "AES-GCM",
iv: iv
}, cryptoKey, new TextEncoder().encode(text))
return btoa(JSON.stringify({
encrypted: arrayBufferToBase64(encrypted),
iv: arrayBufferToBase64(iv)
}))
}
async function importNotes(plaintextNotes) {
try {
for (let i in plaintextNotes) {
plaintextNotes[i]["title"] = await encrypt(plaintextNotes[i]["title"])
plaintextNotes[i]["content"] = await encrypt(plaintextNotes[i]["content"])
}
let purgeNotesFetch = await fetch(remote + "/api/purgenotes", {
method: "POST",
body: JSON.stringify({
"secretKey": secretKey,
}),
headers: {
"Content-Type": "application/json; charset=UTF-8"
}
})
if (purgeNotesFetch.status !== 200) {
return -2
}
let importNotesFetch = await fetch(remote + "/api/importnotes", {
method: "POST",
body: JSON.stringify({
"secretKey": secretKey,
"notes": JSON.stringify(plaintextNotes)
}),
headers: {
"Content-Type": "application/json; charset=UTF-8"
}
})
return importNotesFetch.status
} catch (e) {
return -1
}
}
async function exportNotes() {
try {
titleBox.innerText = "Decrypting Notes..."
let oldPasswordFormat = await hashwasm.sha512(prompt("Please enter your password to decrypt your notes."))
let exportNotesFetch = await fetch(remote + "/api/exportnotes", {
method: "POST",
body: JSON.stringify({
secretKey: secretKey
}),
headers: {
"Content-Type": "application/json; charset=UTF-8"
}
})
let responseData = await exportNotesFetch.json()
for (let i in responseData) {
information.innerText = "Decrypting " + i + "/" + responseData.length
responseData[i]["title"] = CryptoJS.AES.decrypt(responseData[i]["title"], oldPasswordFormat).toString(CryptoJS.enc.Utf8)
responseData[i]["content"] = CryptoJS.AES.decrypt(responseData[i]["content"], oldPasswordFormat).toString(CryptoJS.enc.Utf8)
}
titleBox.innerText = "Notes decrypted!"
information.innerText = "Now if anything goes wrong, you can import this backup to restore your notes in the settings panel."
return responseData
} catch (e) {
titleBox.innerText = "Error!"
information.innerText = "An error occurred while decrypting your notes. Good thing we found out before we started migration! Please try again by pressing continue."
}
}
// @license-end