aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/components/RegisterOrLogin.svelte16
-rw-r--r--src/hooks.server.js2
-rw-r--r--src/lib/socket.js2
-rw-r--r--src/lib/store.js10
-rw-r--r--src/routes/(app)/+layout.server.js2
-rw-r--r--src/routes/(app)/[username]/dashboard/+page.svelte2
-rw-r--r--src/routes/(app)/[username]/generate/+page.svelte61
-rw-r--r--src/routes/(app)/[username]/generate/+server.js8
8 files changed, 90 insertions, 13 deletions
diff --git a/src/components/RegisterOrLogin.svelte b/src/components/RegisterOrLogin.svelte
index 32f046d0..8ee3af4a 100644
--- a/src/components/RegisterOrLogin.svelte
+++ b/src/components/RegisterOrLogin.svelte
@@ -5,7 +5,7 @@
import { enhance } from '$app/forms';
import { Icon } from '$comp';
import { focus } from '$lib/utils';
- import { loginRedirect } from '$lib/store';
+ import { loginRedirect, password } from '$lib/store';
import { t } from '$lib/translations';
import { page } from '$app/stores';
@@ -19,12 +19,12 @@
export let form;
export let pageID;
- let username, password, email, submit;
+ let username, email, btn;
$: update(form);
- let update = (form) => form && ({ username, password } = form);
+ let update = (form) => form && ({ username, password: $password } = form);
- $: token && token?.length === 6 && tick().then(() => submit.click());
+ $: token && token?.length === 6 && tick().then(() => btn.click());
let revealPassword = false;
@@ -52,7 +52,7 @@
<form class="space-y-5" {action} method="POST" use:enhance>
<input type="hidden" name="username" value={username} />
- <input type="hidden" name="password" value={password} />
+ <input type="hidden" name="password" value={$password} />
<input type="hidden" name="token" value={token} />
<div>
@@ -66,7 +66,7 @@
</Pincode>
</div>
<button
- bind:this={submit}
+ bind:this={btn}
type="submit"
class="bg-black text-white w-full rounded-2xl p-4 font-semibold hover:opacity-80"
>{$t('login.' + pageID)}
@@ -107,7 +107,7 @@
type="text"
required
class="bg-primary"
- bind:value={password}
+ bind:value={$password}
autocapitalize="none"
/>
{:else}
@@ -116,7 +116,7 @@
type="password"
required
class="bg-primary"
- bind:value={password}
+ bind:value={$password}
autocapitalize="none"
/>
{/if}
diff --git a/src/hooks.server.js b/src/hooks.server.js
index f6e7ff70..4107ec86 100644
--- a/src/hooks.server.js
+++ b/src/hooks.server.js
@@ -14,8 +14,6 @@ export async function handle({ event, resolve }) {
user = await get('/me', auth(event.cookies));
event.locals.user = user;
- if (!user.pubkey) throw new Error('missing pubkey');
-
if (['/', '/login', '/register'].includes(pathname) && request.method === 'GET')
return Response.redirect(origin + `/${user.username}/dashboard`, 307);
} catch (e) {
diff --git a/src/lib/socket.js b/src/lib/socket.js
index 59c2f872..d43af082 100644
--- a/src/lib/socket.js
+++ b/src/lib/socket.js
@@ -40,7 +40,7 @@ export const messages = (data) => ({
events[data.id] = data;
events.q.push(data.id);
$events.set(events);
- if (events.q.length > 1000) delete events[events.q.shift()];
+ if (events.q.length > 500) delete events[events.q.shift()];
},
rate() {
diff --git a/src/lib/store.js b/src/lib/store.js
index df1525d3..34e8dcca 100644
--- a/src/lib/store.js
+++ b/src/lib/store.js
@@ -26,7 +26,14 @@ const persistLocal = (key, defaultValue) => {
: defaultValue
);
- s.subscribe((v) => browser && localStorage.setItem(key, JSON.stringify(v)));
+ s.subscribe((v) => {
+ try {
+ browser && localStorage.setItem(key, JSON.stringify(v));
+ } catch (e) {
+ console.log('problem setting key', v);
+ console.log(e);
+ }
+ });
return s;
};
@@ -48,3 +55,4 @@ export const request = writable();
export const invoice = writable();
export const txns = persistLocal('transactions', []);
export const events = persistLocal('events', {});
+export const password = writable();
diff --git a/src/routes/(app)/+layout.server.js b/src/routes/(app)/+layout.server.js
index d968be70..88ca1539 100644
--- a/src/routes/(app)/+layout.server.js
+++ b/src/routes/(app)/+layout.server.js
@@ -11,6 +11,8 @@ export async function load({ cookies, locals, request, url, params }) {
let { user } = locals;
if (user) {
+ if (!user.pubkey && !url.pathname.includes('gen'))
+ throw redirect(307, `/${user.username}/generate`);
let account = user.accounts.find((a) => a.asset === btc && !a.pubkey);
if (account) {
let { id } = account;
diff --git a/src/routes/(app)/[username]/dashboard/+page.svelte b/src/routes/(app)/[username]/dashboard/+page.svelte
index c10518f5..0b49a870 100644
--- a/src/routes/(app)/[username]/dashboard/+page.svelte
+++ b/src/routes/(app)/[username]/dashboard/+page.svelte
@@ -3,7 +3,7 @@
import { f, s, post, failure, sat, sats } from '$lib/utils';
import { tick } from 'svelte';
import { Avatar, Icon } from '$comp';
- import { rate, selectedRate } from '$lib/store';
+ import { rate, password, selectedRate } from '$lib/store';
import { goto } from '$app/navigation';
import { t } from '$lib/translations';
diff --git a/src/routes/(app)/[username]/generate/+page.svelte b/src/routes/(app)/[username]/generate/+page.svelte
new file mode 100644
index 00000000..873e336f
--- /dev/null
+++ b/src/routes/(app)/[username]/generate/+page.svelte
@@ -0,0 +1,61 @@
+<script>
+ import { Buffer } from 'buffer';
+ import { onMount } from 'svelte';
+ import { BIP32Factory } from 'bip32';
+ import { bech32m } from 'bech32';
+ import { generateMnemonic, mnemonicToEntropy, mnemonicToSeedSync } from 'bip39';
+ import * as ecc from 'tiny-secp256k1';
+ import { post } from '$lib/utils';
+ import { goto } from '$app/navigation';
+
+ export let data;
+ let { user } = data;
+
+ const { encode, decode, fromWords, toWords } = bech32m;
+
+ const stretch = async (password, salt) =>
+ crypto.subtle.deriveKey(
+ {
+ name: 'PBKDF2',
+ salt,
+ iterations: 100000,
+ hash: 'SHA-256'
+ },
+ await crypto.subtle.importKey(
+ 'raw',
+ new TextEncoder().encode(password),
+ { name: 'PBKDF2' },
+ false,
+ ['deriveBits', 'deriveKey']
+ ),
+ { name: 'AES-GCM', length: 256 },
+ true,
+ ['encrypt', 'decrypt']
+ );
+
+ let mnemonic, key, password, seed, entropy, cipher, pubkey, salt, child;
+
+ onMount(async () => {
+ let bip32 = await BIP32Factory(ecc);
+ mnemonic = generateMnemonic();
+ seed = mnemonicToSeedSync(mnemonic);
+ entropy = mnemonicToEntropy(mnemonic);
+ key = bip32.fromSeed(seed);
+ child = key.derivePath("m/44'/1237'/0'/0/0");
+ salt = crypto.getRandomValues(new Uint8Array(16));
+
+ let bytes = new Uint8Array(
+ await crypto.subtle.encrypt(
+ { name: 'AES-GCM', iv: new Uint8Array(16) },
+ await stretch(password, salt),
+ Uint8Array.from(Buffer.from(entropy, 'hex'))
+ )
+ );
+
+ user.pubkey = Buffer.from(ecc.xOnlyPointFromPoint(child.publicKey)).toString('hex');
+ user.cipher = encode('encprv', toWords(bytes), 180);
+
+ await post(`/${user.username}/generate`, user);
+ goto(`/${user.username}/dashboard`);
+ });
+</script>
diff --git a/src/routes/(app)/[username]/generate/+server.js b/src/routes/(app)/[username]/generate/+server.js
new file mode 100644
index 00000000..c63ab5a6
--- /dev/null
+++ b/src/routes/(app)/[username]/generate/+server.js
@@ -0,0 +1,8 @@
+import { auth, post } from '$lib/utils';
+import { json } from '@sveltejs/kit';
+
+export async function POST({ cookies, request }) {
+ let body = await request.json();
+ let user = await post(`/user`, body, auth(cookies));
+ return json(user);
+}