set up service worker caching, fix perf issues with rerenders, split code into more modules, switch to a green theme, add modal for updating site via service worker

This commit is contained in:
2024-02-02 17:25:32 -05:00
parent ccfe6c60cd
commit ea4c8a3a30
28 changed files with 5278 additions and 480 deletions

View File

@@ -1,3 +1,5 @@
import { LOCAL_STORAGE_KEYS } from "./store";
export async function generateKeyPair(): Promise<CryptoKeyPair> {
return await window.crypto.subtle.generateKey(
{
@@ -34,11 +36,15 @@ export async function retrieveOrGenerateKeyPair(): Promise<CryptoKeyPair> {
let ecdhPrivate: CryptoKey;
if (
localStorage.getItem("ecdhPublic") &&
localStorage.getItem("ecdhPrivate")
localStorage.getItem(LOCAL_STORAGE_KEYS.ECDH_PUBLIC_KEY) &&
localStorage.getItem(LOCAL_STORAGE_KEYS.ECDH_PRIVATE_KEY)
) {
const ecdhPublicJwk = JSON.parse(localStorage.getItem("ecdhPublic")!);
const ecdhPrivateJwk = JSON.parse(localStorage.getItem("ecdhPrivate")!);
const ecdhPublicJwk = JSON.parse(
localStorage.getItem(LOCAL_STORAGE_KEYS.ECDH_PUBLIC_KEY)!
);
const ecdhPrivateJwk = JSON.parse(
localStorage.getItem(LOCAL_STORAGE_KEYS.ECDH_PRIVATE_KEY)!
);
ecdhPrivate = await window.crypto.subtle.importKey(
"jwk",
@@ -78,8 +84,14 @@ async function saveKeys(ecdhPublic: CryptoKey, ecdhPrivate: CryptoKey) {
);
// Store ECDH key pair in local storage
localStorage.setItem("ecdhPublic", JSON.stringify(ecdhPublicJwk));
localStorage.setItem("ecdhPrivate", JSON.stringify(ecdhPrivateJwk));
localStorage.setItem(
LOCAL_STORAGE_KEYS.ECDH_PUBLIC_KEY,
JSON.stringify(ecdhPublicJwk)
);
localStorage.setItem(
LOCAL_STORAGE_KEYS.ECDH_PRIVATE_KEY,
JSON.stringify(ecdhPrivateJwk)
);
return {
public: JSON.stringify(ecdhPublicJwk),
@@ -116,7 +128,6 @@ export async function decrypt(
iv: string,
encryptedData: string
): Promise<string> {
console.log(iv);
// Decode the iv and encryptedData from base64
const ivUint8Array = base64ToUint8Array(iv);
const encryptedDataUint8Array = base64ToUint8Array(encryptedData);

View File

@@ -1,83 +0,0 @@
import { html } from "uhtml";
import {
exportKeys,
importAndSaveKeys,
retrieveOrGenerateKeyPair,
} from "./crypto";
import { reactive } from "uhtml/reactive";
import { effect, signal } from "uhtml/preactive";
const render = reactive(effect);
const ecdhPublicKey = signal(localStorage.getItem("ecdhPublic"));
const ecdhPrivateKey = signal(localStorage.getItem("ecdhPrivate"));
const MODAL_ID = "keyManagerModal";
const template = html` <dialog class="key-manager" .id=${MODAL_ID}>
<article>
<header>Key Manager</header>
<p>
If you'd like to manage your keys, you can do so here. You can export your keys to transfer to a different
device, or import keys exported from a different device.
</p>
<p>
Public Key:
<pre>${ecdhPublicKey}</pre>
</p>
<p>
Private Key:
<pre>${ecdhPrivateKey}</pre>
</p>
<p>
<button @click=${exportKeys}>Export Keys</button>
<button @click=${importKeys}>Import Keys</button>
<input type="file" accept=".json" style="display: none" id="keypairImportEl" @change=${handleKeypairImport} />
<button @click=${close}>Close</button>
</p>
</article>
</dialog>`;
function importKeys(): void {
const el = document.querySelector("#keypairImportEl");
if (!el) {
throw new Error("No file input found");
}
(el as HTMLElement).click();
}
function handleKeypairImport(event: Event): void {
const fileInput = event.target as HTMLInputElement;
const file = fileInput.files?.[0];
if (!file) return;
const reader = new FileReader();
reader.onload = async (event) => {
const keys = JSON.parse(event.target?.result as string);
await importAndSaveKeys(keys);
alert("Successfully imported keypair");
};
reader.readAsText(file);
}
function close(event: Event): void {
const dialog = (event.target as HTMLElement).closest(`#${MODAL_ID}`);
dialog?.removeAttribute("open");
}
export const initKeyManager = (target: HTMLElement | null) => {
const openKeyManagerEl = document.querySelector("#openKeyManager");
if (!target || !openKeyManagerEl) {
throw new Error("No target element found");
}
retrieveOrGenerateKeyPair();
openKeyManagerEl.addEventListener("click", (event: Event) => {
event.preventDefault();
ecdhPrivateKey.value = localStorage.getItem("ecdhPrivate");
ecdhPublicKey.value = localStorage.getItem("ecdhPublic");
document.querySelector(`#${MODAL_ID}`)?.setAttribute("open", "");
});
render(target, template);
};

14
src/utils/store.ts Normal file
View File

@@ -0,0 +1,14 @@
import { signal } from "uhtml/preactive";
import { Route } from "../router";
// the current page / params for this URL
export const page = signal(Route.Receive);
export const params = signal(
new URLSearchParams(window.location.hash.slice(1))
);
export enum LOCAL_STORAGE_KEYS {
REQUEST_PUBLIC_KEY = "requestPublicKey",
ECDH_PUBLIC_KEY = "ecdhPublic",
ECDH_PRIVATE_KEY = "ecdhPrivate",
}

1
src/utils/vite-env.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
/// <reference types="vite/client" />

1
src/utils/vite-plugin-pwa.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
/// <reference types="vite-plugin-pwa/client" />