forked from coracle/flotilla
Split key recovery components, bump deps
This commit is contained in:
+12
-12
@@ -61,16 +61,16 @@
|
||||
"@types/throttle-debounce": "^5.0.2",
|
||||
"@vite-pwa/assets-generator": "^0.2.6",
|
||||
"@vite-pwa/sveltekit": "^0.6.8",
|
||||
"@welshman/app": "^0.7.1",
|
||||
"@welshman/content": "^0.7.1",
|
||||
"@welshman/editor": "^0.7.1",
|
||||
"@welshman/feeds": "^0.7.1",
|
||||
"@welshman/lib": "^0.7.1",
|
||||
"@welshman/net": "^0.7.1",
|
||||
"@welshman/router": "^0.7.1",
|
||||
"@welshman/signer": "^0.7.1",
|
||||
"@welshman/store": "^0.7.1",
|
||||
"@welshman/util": "^0.7.1",
|
||||
"@welshman/app": "^0.8.0-pre.1",
|
||||
"@welshman/content": "^0.8.0-pre.1",
|
||||
"@welshman/editor": "^0.8.0-pre.1",
|
||||
"@welshman/feeds": "^0.8.0-pre.1",
|
||||
"@welshman/lib": "^0.8.0-pre.1",
|
||||
"@welshman/net": "^0.8.0-pre.1",
|
||||
"@welshman/router": "^0.8.0-pre.1",
|
||||
"@welshman/signer": "^0.8.0-pre.1",
|
||||
"@welshman/store": "^0.8.0-pre.1",
|
||||
"@welshman/util": "^0.8.0-pre.1",
|
||||
"compressorjs": "^1.2.1",
|
||||
"daisyui": "^4.12.24",
|
||||
"date-picker-svelte": "^2.16.0",
|
||||
@@ -80,13 +80,13 @@
|
||||
"husky": "^9.1.7",
|
||||
"idb": "^8.0.3",
|
||||
"nostr-signer-capacitor-plugin": "^0.0.4",
|
||||
"nostr-tools": "^2.14.2",
|
||||
"nostr-tools": "^2.19.4",
|
||||
"prettier-plugin-tailwindcss": "^0.6.14",
|
||||
"qr-scanner": "^1.4.2",
|
||||
"qrcode": "^1.5.4",
|
||||
"throttle-debounce": "^5.0.2",
|
||||
"tippy.js": "^6.3.7",
|
||||
"@pomade/core": "^0.0.7"
|
||||
"@pomade/core": "^0.0.9"
|
||||
},
|
||||
"pnpm": {
|
||||
"ignoredBuiltDependencies": [
|
||||
|
||||
@@ -25,7 +25,10 @@
|
||||
|
||||
{#if url.match(/\.(jpe?g|png|gif|webp)$/)}
|
||||
<!-- Use a real link so people can copy the href -->
|
||||
<a href={url} class="link-content whitespace-nowrap" onclick={stopPropagation(preventDefault(expand))}>
|
||||
<a
|
||||
href={url}
|
||||
class="link-content whitespace-nowrap"
|
||||
onclick={stopPropagation(preventDefault(expand))}>
|
||||
<Icon icon={LinkRound} size={3} class="inline-block" />
|
||||
{displayUrl(url)}
|
||||
</a>
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
import {encrypt} from "nostr-tools/nip49"
|
||||
import {hexToBytes} from "@welshman/lib"
|
||||
import {makeSecret} from "@welshman/util"
|
||||
import type {Profile} from "@welshman/util"
|
||||
import {preventDefault, downloadText} from "@lib/html"
|
||||
import Key from "@assets/icons/key-minimalistic.svg?dataurl"
|
||||
import ArrowDown from "@assets/icons/arrow-down.svg?dataurl"
|
||||
@@ -14,9 +13,7 @@
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||
import SignUpComplete from "@app/components/SignUpComplete.svelte"
|
||||
import {pushToast} from "@app/util/toast"
|
||||
import {pushModal} from "@app/util/modal"
|
||||
import {PLATFORM_NAME} from "@app/core/state"
|
||||
|
||||
type Props = {
|
||||
|
||||
@@ -1,141 +0,0 @@
|
||||
<script lang="ts">
|
||||
import {Client, decodeChallenge} from '@pomade/core'
|
||||
import {chunk, sleep, uniq, identity, tryCatch} from "@welshman/lib"
|
||||
import {
|
||||
makeEvent,
|
||||
createProfile,
|
||||
PROFILE,
|
||||
isReplaceable,
|
||||
getAddress,
|
||||
RelayMode,
|
||||
getPubkey,
|
||||
} from "@welshman/util"
|
||||
import type {SessionPomade} from '@welshman/app'
|
||||
import {pubkey, session, publishThunk, repository, derivePubkeyRelays} from "@welshman/app"
|
||||
import {preventDefault} from "@lib/html"
|
||||
import AltArrowLeft from "@assets/icons/alt-arrow-left.svg?dataurl"
|
||||
import AltArrowRight from "@assets/icons/alt-arrow-right.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import Spinner from "@lib/components/Spinner.svelte"
|
||||
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||
import KeyDownload from "@app/components/KeyDownload.svelte"
|
||||
import {pushToast} from "@app/util/toast"
|
||||
import {pushModal, clearModals} from "@app/util/modal"
|
||||
import {logout} from "@app/core/commands"
|
||||
import {INDEXER_RELAYS, POMADE_SIGNERS, PLATFORM_NAME, userSpaceUrls} from "@app/core/state"
|
||||
|
||||
const {email, clientOptions: {secret, peers}} = $session as SessionPomade
|
||||
|
||||
const requestRecovery = async () => {
|
||||
await Client.requestChallenge(email, peers)
|
||||
sent = true
|
||||
}
|
||||
|
||||
const confirmRecovery = async () => {
|
||||
const challenges = input.split(/\n/).map(x => x.trim()).filter(x => tryCatch(() => decodeChallenge(x)))
|
||||
|
||||
if (challenges.length < 2) {
|
||||
return pushToast({
|
||||
theme: "error",
|
||||
message: "Failed to recover, not enough valid challenges were provided."
|
||||
})
|
||||
}
|
||||
|
||||
const request = await Client.recoverWithChallenge(email, challenges)
|
||||
|
||||
if (!request.ok) {
|
||||
console.log(request.messages)
|
||||
|
||||
return pushToast({
|
||||
theme: "error",
|
||||
message: `Failed to recover: ${request.messages[0]?.payload.message.toLowerCase()}`
|
||||
})
|
||||
}
|
||||
|
||||
const result = await Client.selectRecovery(request.clientSecret, getPubkey(secret), peers)
|
||||
|
||||
if (!result.ok) {
|
||||
console.log(result.messages)
|
||||
|
||||
return pushToast({
|
||||
theme: "error",
|
||||
message: `Failed to recover: ${result.messages[0]?.payload.message.toLowerCase()}`
|
||||
})
|
||||
}
|
||||
|
||||
pushModal(KeyDownload, {secret: result.userSecret, next: clearModals, submitText: "Done"})
|
||||
}
|
||||
|
||||
const submit = async () => {
|
||||
loading = true
|
||||
|
||||
try {
|
||||
if (sent) {
|
||||
await confirmRecovery()
|
||||
} else {
|
||||
await requestRecovery()
|
||||
}
|
||||
} finally {
|
||||
loading = false
|
||||
}
|
||||
}
|
||||
|
||||
const back = () => history.back()
|
||||
|
||||
let sent = $state(false)
|
||||
let loading = $state(false)
|
||||
let input = $state("")
|
||||
let userSecret = $state("")
|
||||
</script>
|
||||
|
||||
<form class="column gap-4" onsubmit={preventDefault(submit)}>
|
||||
<ModalHeader>
|
||||
{#snippet title()}
|
||||
Recover your Key
|
||||
{/snippet}
|
||||
{#snippet info()}
|
||||
Take control over your cryptographic identity
|
||||
{/snippet}
|
||||
</ModalHeader>
|
||||
{#if sent}
|
||||
<p>
|
||||
Your recovery codes have been sent!
|
||||
</p>
|
||||
<p>
|
||||
For security reasons, you may receive three or more emails with recovery codes in them. Please paste <i>all</i>
|
||||
recovery codes into the text box below, on separate lines.
|
||||
</p>
|
||||
<textarea
|
||||
rows={POMADE_SIGNERS.length + 1}
|
||||
class="textarea textarea-bordered leading-4 text-xs"
|
||||
bind:value={input}></textarea>
|
||||
{:else}
|
||||
<p>
|
||||
When you signed up, your Nostr secret key was split into multiple pieces and stored on separate
|
||||
third-party servers to keep it safe.
|
||||
</p>
|
||||
<p>
|
||||
If you're ready to take control of your cryptographic identity, click below. We'll confirm your
|
||||
email by sending you some recovery codes.
|
||||
</p>
|
||||
{/if}
|
||||
<ModalFooter>
|
||||
<Button class="btn btn-link" onclick={back}>
|
||||
<Icon icon={AltArrowLeft} />
|
||||
Go back
|
||||
</Button>
|
||||
{#if sent}
|
||||
<Button type="submit" class="btn btn-primary" disabled={loading}>
|
||||
<Spinner {loading}>Confirm recovery</Spinner>
|
||||
<Icon icon={AltArrowRight} />
|
||||
</Button>
|
||||
{:else}
|
||||
<Button type="submit" class="btn btn-primary" disabled={loading}>
|
||||
<Spinner {loading}>Request recovery</Spinner>
|
||||
<Icon icon={AltArrowRight} />
|
||||
</Button>
|
||||
{/if}
|
||||
</ModalFooter>
|
||||
</form>
|
||||
@@ -0,0 +1,108 @@
|
||||
<script lang="ts">
|
||||
import {Client, decodeChallenge} from "@pomade/core"
|
||||
import {tryCatch} from "@welshman/lib"
|
||||
import {getPubkey} from "@welshman/util"
|
||||
import type {SessionPomade} from "@welshman/app"
|
||||
import {session} from "@welshman/app"
|
||||
import {preventDefault} from "@lib/html"
|
||||
import AltArrowLeft from "@assets/icons/alt-arrow-left.svg?dataurl"
|
||||
import AltArrowRight from "@assets/icons/alt-arrow-right.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import Spinner from "@lib/components/Spinner.svelte"
|
||||
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||
import KeyDownload from "@app/components/KeyDownload.svelte"
|
||||
import {pushToast} from "@app/util/toast"
|
||||
import {pushModal, clearModals} from "@app/util/modal"
|
||||
import {POMADE_SIGNERS} from "@app/core/state"
|
||||
|
||||
const {
|
||||
email,
|
||||
clientOptions: {secret, peers},
|
||||
} = $session as SessionPomade
|
||||
|
||||
const confirmRecovery = async () => {
|
||||
const challenges = input
|
||||
.split(/\n/)
|
||||
.map(x => x.trim())
|
||||
.filter(x => tryCatch(() => decodeChallenge(x)))
|
||||
|
||||
if (challenges.length < 2) {
|
||||
return pushToast({
|
||||
theme: "error",
|
||||
message: "Failed to recover, not enough valid challenges were provided.",
|
||||
})
|
||||
}
|
||||
|
||||
const request = await Client.recoverWithChallenge(email, challenges)
|
||||
|
||||
if (!request.ok) {
|
||||
console.log(request.messages)
|
||||
|
||||
return pushToast({
|
||||
theme: "error",
|
||||
message: `Failed to recover: ${request.messages[0]?.payload.message.toLowerCase()}`,
|
||||
})
|
||||
}
|
||||
|
||||
const result = await Client.selectRecovery(request.clientSecret, getPubkey(secret), peers)
|
||||
|
||||
if (!result.ok) {
|
||||
console.log(result.messages)
|
||||
|
||||
return pushToast({
|
||||
theme: "error",
|
||||
message: `Failed to recover: ${result.messages[0]?.payload.message.toLowerCase()}`,
|
||||
})
|
||||
}
|
||||
|
||||
pushModal(KeyDownload, {secret: result.userSecret, next: clearModals, submitText: "Done"})
|
||||
}
|
||||
|
||||
const submit = async () => {
|
||||
loading = true
|
||||
|
||||
try {
|
||||
await confirmRecovery()
|
||||
} finally {
|
||||
loading = false
|
||||
}
|
||||
}
|
||||
|
||||
const back = () => history.back()
|
||||
|
||||
let loading = $state(false)
|
||||
let input = $state("")
|
||||
</script>
|
||||
|
||||
<form class="column gap-4" onsubmit={preventDefault(submit)}>
|
||||
<ModalHeader>
|
||||
{#snippet title()}
|
||||
Recover your Key
|
||||
{/snippet}
|
||||
{#snippet info()}
|
||||
Take control over your cryptographic identity
|
||||
{/snippet}
|
||||
</ModalHeader>
|
||||
<p>Your recovery codes have been sent!</p>
|
||||
<p>
|
||||
For security reasons, you may receive three or more emails with recovery codes in them. Please
|
||||
paste <i>all</i>
|
||||
recovery codes into the text box below, on separate lines.
|
||||
</p>
|
||||
<textarea
|
||||
rows={POMADE_SIGNERS.length + 1}
|
||||
class="textarea textarea-bordered text-xs leading-4"
|
||||
bind:value={input}></textarea>
|
||||
<ModalFooter>
|
||||
<Button class="btn btn-link" onclick={back}>
|
||||
<Icon icon={AltArrowLeft} />
|
||||
Go back
|
||||
</Button>
|
||||
<Button type="submit" class="btn btn-primary" disabled={loading}>
|
||||
<Spinner {loading}>Confirm recovery</Spinner>
|
||||
<Icon icon={AltArrowRight} />
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</form>
|
||||
@@ -0,0 +1,68 @@
|
||||
<script lang="ts">
|
||||
import {Client} from "@pomade/core"
|
||||
import type {SessionPomade} from "@welshman/app"
|
||||
import {session} from "@welshman/app"
|
||||
import {preventDefault} from "@lib/html"
|
||||
import AltArrowLeft from "@assets/icons/alt-arrow-left.svg?dataurl"
|
||||
import AltArrowRight from "@assets/icons/alt-arrow-right.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import Spinner from "@lib/components/Spinner.svelte"
|
||||
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||
import {pushModal} from "@app/util/modal"
|
||||
import KeyRecoveryConfirm from "@app/components/KeyRecoveryConfirm.svelte"
|
||||
|
||||
const {
|
||||
email,
|
||||
clientOptions: {peers},
|
||||
} = $session as SessionPomade
|
||||
|
||||
const requestRecovery = async () => {
|
||||
await Client.requestChallenge(email, peers)
|
||||
pushModal(KeyRecoveryConfirm)
|
||||
}
|
||||
|
||||
const submit = async () => {
|
||||
loading = true
|
||||
|
||||
try {
|
||||
await requestRecovery()
|
||||
} finally {
|
||||
loading = false
|
||||
}
|
||||
}
|
||||
|
||||
const back = () => history.back()
|
||||
|
||||
let loading = $state(false)
|
||||
</script>
|
||||
|
||||
<form class="column gap-4" onsubmit={preventDefault(submit)}>
|
||||
<ModalHeader>
|
||||
{#snippet title()}
|
||||
Recover your Key
|
||||
{/snippet}
|
||||
{#snippet info()}
|
||||
Take control over your cryptographic identity
|
||||
{/snippet}
|
||||
</ModalHeader>
|
||||
<p>
|
||||
When you signed up, your Nostr secret key was split into multiple pieces and stored on separate
|
||||
third-party servers to keep it safe.
|
||||
</p>
|
||||
<p>
|
||||
If you're ready to take control of your cryptographic identity, click below. We'll confirm your
|
||||
email by sending you some recovery codes.
|
||||
</p>
|
||||
<ModalFooter>
|
||||
<Button class="btn btn-link" onclick={back}>
|
||||
<Icon icon={AltArrowLeft} />
|
||||
Go back
|
||||
</Button>
|
||||
<Button type="submit" class="btn btn-primary" disabled={loading}>
|
||||
<Spinner {loading}>Request recovery</Spinner>
|
||||
<Icon icon={AltArrowRight} />
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</form>
|
||||
@@ -19,6 +19,7 @@
|
||||
try {
|
||||
const client = new Client($session.clientOptions)
|
||||
const result = await client.listSessions()
|
||||
const pubkey = await client.getPubkey()
|
||||
|
||||
if (result.ok) {
|
||||
// Group sessions by client pubkey and collect peers
|
||||
@@ -34,15 +35,13 @@
|
||||
|
||||
if (existing) {
|
||||
existing.peers.push(peer)
|
||||
} else {
|
||||
} else if (item.client !== pubkey) {
|
||||
sessionMap.set(item.client, {...item, peers: [peer]})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sessions = Array.from(sessionMap.values()).filter(
|
||||
s => s.client !== client.pubkey, // Filter out current session
|
||||
)
|
||||
sessions = Array.from(sessionMap.values())
|
||||
} else {
|
||||
pushToast({
|
||||
theme: "error",
|
||||
|
||||
@@ -39,7 +39,9 @@
|
||||
let client: Client | undefined = undefined
|
||||
|
||||
try {
|
||||
const {ok, clientOptions} = await Client.register(2, 3, makeSecret())
|
||||
const userSecret = makeSecret()
|
||||
console.log(userSecret)
|
||||
const {ok, clientOptions} = await Client.register(2, 3, userSecret)
|
||||
|
||||
if (!ok) {
|
||||
return pushToast({
|
||||
|
||||
+13
-2
@@ -1,7 +1,18 @@
|
||||
import {page} from "$app/stores"
|
||||
import type {Unsubscriber} from "svelte/store"
|
||||
import {derived, get} from "svelte/store"
|
||||
import {partition, call, sortBy, assoc, dissoc, chunk, sleep, identity, WEEK, ago} from "@welshman/lib"
|
||||
import {
|
||||
partition,
|
||||
call,
|
||||
sortBy,
|
||||
assoc,
|
||||
dissoc,
|
||||
chunk,
|
||||
sleep,
|
||||
identity,
|
||||
WEEK,
|
||||
ago,
|
||||
} from "@welshman/lib"
|
||||
import {
|
||||
getListTags,
|
||||
getRelayTagValues,
|
||||
@@ -94,7 +105,7 @@ const pullAndListen = ({relays, filters, signal}: PullOpts) => {
|
||||
request({
|
||||
relays,
|
||||
signal,
|
||||
filters: unionFilters(filters.map(dissoc('limit'))).map(assoc("limit", 0)),
|
||||
filters: unionFilters(filters.map(dissoc("limit"))).map(assoc("limit", 0)),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
import ContentMinimal from "@app/components/ContentMinimal.svelte"
|
||||
import ProfileEdit from "@app/components/ProfileEdit.svelte"
|
||||
import ProfileDelete from "@app/components/ProfileDelete.svelte"
|
||||
import KeyRecovery from "@app/components/KeyRecovery.svelte"
|
||||
import KeyRecoveryRequest from "@app/components/KeyRecoveryRequest.svelte"
|
||||
import SignerStatus from "@app/components/SignerStatus.svelte"
|
||||
import InfoKeys from "@app/components/InfoKeys.svelte"
|
||||
import {PLATFORM_NAME} from "@app/core/state"
|
||||
@@ -41,7 +41,7 @@
|
||||
|
||||
const startDelete = () => pushModal(ProfileDelete)
|
||||
|
||||
const startRecovery = () => pushModal(KeyRecovery)
|
||||
const startRecovery = () => pushModal(KeyRecoveryRequest)
|
||||
|
||||
let showAdvanced = false
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user