diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/lib/utils.js | 24 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/+page.server.js | 9 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/+page.svelte | 60 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/+server.js | 6 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/generate/+page.svelte | 27 |
5 files changed, 98 insertions, 28 deletions
diff --git a/src/lib/utils.js b/src/lib/utils.js index f59b6359..14646ad0 100644 --- a/src/lib/utils.js +++ b/src/lib/utils.js @@ -1,3 +1,5 @@ +import * as ecc from 'tiny-secp256k1'; +import { BIP32Factory } from 'bip32'; import { browser } from '$app/environment'; import { toast } from '@zerodevx/svelte-toast'; import { goto } from '$app/navigation'; @@ -168,3 +170,25 @@ export const wait = async (f, s = 300) => { if (i >= s) throw new Error('timeout'); return f(); }; + +export 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'] + ); + +export const bip32 = await BIP32Factory(ecc); diff --git a/src/routes/(app)/[username]/+page.server.js b/src/routes/(app)/[username]/+page.server.js index 01dfab5a..547f754f 100644 --- a/src/routes/(app)/[username]/+page.server.js +++ b/src/routes/(app)/[username]/+page.server.js @@ -1,14 +1,19 @@ import { get } from '$lib/utils'; import Qr from 'qrcode-base64'; -export async function load({ params, url }) { +export async function load({ params, parent, url }) { + let { user } = await parent(); + let pubkey; + if (user) ({ pubkey } = user); + else pubkey = params.username; + let { username } = params; let text = `${encodeURI(username)}@${url.hostname}`; let src = Qr.drawImg(text, { size: 300 }); let events = []; try { - events = await get(`/${username}/events`); + events = await get(`/${pubkey}/events`); } catch (e) { console.log(e); } diff --git a/src/routes/(app)/[username]/+page.svelte b/src/routes/(app)/[username]/+page.svelte index 78d84653..837257ef 100644 --- a/src/routes/(app)/[username]/+page.svelte +++ b/src/routes/(app)/[username]/+page.svelte @@ -1,12 +1,53 @@ <script> + import { Buffer } from 'buffer'; + import { entropyToMnemonic, mnemonicToSeedSync } from 'bip39'; + import { enhance } from '$app/forms'; import { Feed, Icon, PageNotFound } from '$comp'; import { t } from '$lib/translations'; + import { bech32m } from 'bech32'; + import { calculateId, signId } from 'nostr'; + import { bip32, stretch, post } from '$lib/utils'; + + const { decode, fromWords } = bech32m; export let data; + let message; + let { events, user, subject, src, text } = data; let followed = false; let { username: n } = subject; + + let submit = async () => { + let mnemonic, key, password, seed, entropy, child, privkey; + entropy = Buffer.from( + await crypto.subtle.decrypt( + { name: 'AES-GCM', iv: new Uint8Array(16) }, + await stretch(password, Buffer.from(user.salt, 'hex')), + Uint8Array.from(fromWords(decode(user.cipher, 180).words)) + ), + 'hex' + ).toString('hex'); + + mnemonic = entropyToMnemonic(entropy); + seed = mnemonicToSeedSync(mnemonic); + key = bip32.fromSeed(seed); + child = key.derivePath("m/44'/1237'/0'/0/0"); + privkey = child.privateKey; + + let event = { + pubkey: user.pubkey, + created_at: Math.floor(Date.now() / 1000), + kind: 1, + content: message, + tags: [] + }; + + event.id = await calculateId(event); + event.sig = await signId(privkey, event.id); + await post(`/${user.username}`, { event }); + }; + $: username = n.length > 60 ? n.substr(0, 6) : n; </script> @@ -69,9 +110,24 @@ </div> </div> - <div class="w-full flex md:pt-12 lg:pt-0"> + <div class="w-full md:pt-12 lg:pt-0"> + {#if user.username === n} + <form on:submit|preventDefault={submit}> + <input + name="message" + bind:value={message} + placeholder="What's on your mind?" + class="w-full my-5 max-w-lg mx-auto" + /> + </form> + {/if} + <div class="mx-auto space-y-5 mt-5 lg:max-w-lg xl:max-w-2xl"> - <Feed {events} /> + {#if events.length} + <Feed {events} /> + {:else} + <div class="text-center">You haven't posted anything yet</div> + {/if} </div> </div> </div> diff --git a/src/routes/(app)/[username]/+server.js b/src/routes/(app)/[username]/+server.js new file mode 100644 index 00000000..d0b1242f --- /dev/null +++ b/src/routes/(app)/[username]/+server.js @@ -0,0 +1,6 @@ +import { auth, post } from '$lib/utils'; +import { json } from '@sveltejs/kit'; + +export async function POST({ cookies, request }) { + return json(post('/nostr/send', await request.json(), auth(cookies))); +} diff --git a/src/routes/(app)/[username]/generate/+page.svelte b/src/routes/(app)/[username]/generate/+page.svelte index ee67237c..fd6b6f7e 100644 --- a/src/routes/(app)/[username]/generate/+page.svelte +++ b/src/routes/(app)/[username]/generate/+page.svelte @@ -1,42 +1,20 @@ <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 { bip32, stretch, 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'] - ); + const { encode, toWords } = bech32m; 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); @@ -54,6 +32,7 @@ user.pubkey = Buffer.from(ecc.xOnlyPointFromPoint(child.publicKey)).toString('hex'); user.cipher = encode('en', toWords(bytes), 180); + user.salt = Buffer.from(salt).toString('hex'); await post(`/${user.username}/generate`, user); goto(`/${user.username}/dashboard`); |
