aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2025-01-03 15:24:31 -0800
committerAdam Soltys <[email protected]>2025-01-03 15:24:31 -0800
commit50296ccde06dc7892b0c6bd1546030f495564b28 (patch)
tree444e26008235c9a57ec2756d8b8fa60133d4e4a9
parent1bfdc18184fd57b741f5ebdfb0e323747f4d06b0 (diff)
downloadcoinos-ui-50296ccde06dc7892b0c6bd1546030f495564b28.tar.gz
coinos-ui-50296ccde06dc7892b0c6bd1546030f495564b28.zip
nostr sign in
-rwxr-xr-xbun.lockbbin187602 -> 192133 bytes
-rw-r--r--package.json3
-rw-r--r--src/components/Nostr.svelte336
-rw-r--r--src/locales/en.json3
-rw-r--r--src/routes/(app)/[username]/(profile)/+page.svelte15
-rw-r--r--src/routes/(app)/events/+server.ts8
-rw-r--r--src/routes/(app)/invoice/[id]/paid/+page.svelte11
-rw-r--r--src/routes/(app)/ln/[lnurl]/+page.server.ts2
-rw-r--r--src/routes/(app)/pay/[username]/[...amount]/+page.server.ts6
-rw-r--r--src/routes/(app)/send/invoice/[id]/+page.server.ts4
-rw-r--r--src/routes/(static)/login/+page.server.ts2
-rw-r--r--src/routes/(static)/login/+page.svelte62
-rw-r--r--src/routes/amber/[id]/[pubkey]/+server.ts6
13 files changed, 391 insertions, 67 deletions
diff --git a/bun.lockb b/bun.lockb
index d2028a53..b80345e2 100755
--- a/bun.lockb
+++ b/bun.lockb
Binary files differ
diff --git a/package.json b/package.json
index 07942ef6..a22d3954 100644
--- a/package.json
+++ b/package.json
@@ -16,6 +16,7 @@
"@sveltejs/vite-plugin-svelte": "^4.0.0",
"@types/uuid": "^10.0.0",
"@zerodevx/svelte-toast": "^0.9.5",
+ "applesauce-signer": "^0.10.0",
"autoprefixer": "^10.4.13",
"bech32": "^2.0.0",
"bip21": "^2.0.3",
@@ -30,7 +31,7 @@
"leaflet": "^1.9.3",
"leaflet.markercluster": "^1.5.3",
"maplibre-gl": "^3.3.1",
- "nostr-tools": "^2.10.3",
+ "nostr-tools": "^2.10.4",
"postcss": "^8.4.20",
"prettier": "^3.1.0",
"prettier-plugin-svelte": "^3.2.6",
diff --git a/src/components/Nostr.svelte b/src/components/Nostr.svelte
new file mode 100644
index 00000000..6445c4c1
--- /dev/null
+++ b/src/components/Nostr.svelte
@@ -0,0 +1,336 @@
+<script lang="ts">
+ import { applyAction, deserialize } from "$app/forms";
+ import { onMount } from "svelte";
+ import { browser } from "$app/environment";
+ import { t } from "$lib/translations";
+ import PasswordInput from "$comp/PasswordInput.svelte";
+ import { Relay } from "nostr-tools/relay";
+ import { encrypt, decrypt, getConversationKey } from "nostr-tools/nip44";
+ import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
+ import {
+ nip19,
+ getEventHash,
+ finalizeEvent,
+ generateSecretKey,
+ getPublicKey,
+ } from "nostr-tools";
+ import { AmberClipboardSigner } from "applesauce-signer";
+
+ import { focus, fail, post } from "$lib/utils";
+ import { password as pw, passwordPrompt } from "$lib/store";
+
+ let { id, redirect, token, nostrSignin } = $props();
+ let extension = $state();
+
+ onMount(() => {
+ if (browser && window.nostr) extension = true;
+ });
+
+ let cancel = () => (nostrSignin = false);
+
+ let sk = $state();
+ let ck = $state();
+ let pk = $state();
+ let relay = $state();
+
+ let signer = $derived(
+ browser &&
+ navigator.userAgent.includes("Android") &&
+ navigator.clipboard &&
+ navigator.clipboard.readText,
+ );
+
+ let connectUrl = $state();
+ // let bunkerConnect = async () => {
+ // sk = generateSecretKey();
+ // console.log("SK", bytesToHex(sk));
+ // let pubkey = getPublicKey(sk);
+ // console.log("PUBKEY", pubkey);
+ //
+ // const url = new URL(bunker);
+ // const secret = url.searchParams.get("secret");
+ // console.log("URL", url);
+ // pk = url.host || url.pathname.replace("//", "");
+ // console.log("PK", pk);
+ // const relayUrl = url.searchParams.get("relay");
+ // relay = await Relay.connect(relayUrl);
+ // console.log("connecting to", relayUrl);
+ //
+ // ck = getConversationKey(sk, pk);
+ //
+ // console.log("PK", pk);
+ // console.log("CK", bytesToHex(ck));
+ //
+ // let connect = {
+ // id: crypto.randomUUID(),
+ // method: "connect",
+ // params: [pk, secret, "get_public_key,sign_event"],
+ // };
+ //
+ // let event = {
+ // created_at: Math.round(Date.now() / 1000),
+ // kind: 24133,
+ // pubkey,
+ // content: encrypt(JSON.stringify(connect), ck),
+ // tags: [["p", pk]],
+ // };
+ //
+ // let signedEvent = finalizeEvent(event, sk);
+ // console.log("publishing connection event", signedEvent);
+ // await relay.publish(signedEvent);
+ //
+ // const sub = relay.subscribe([{ kinds: [24133], "#p": [pubkey] }], {
+ // async onevent(ev) {
+ // console.log("got event:", ev);
+ // try {
+ // console.log(decrypt(ev.content, ck));
+ // } catch (e) {
+ // console.log(e);
+ // }
+ // },
+ // });
+ // };
+
+ let getPubkey = async () => {
+ let pubkey = getPublicKey(sk);
+ let getPubkey = {
+ id: crypto.randomUUID(),
+ method: "get_public_key",
+ params: [],
+ };
+
+ let testEvent = {
+ created_at: Math.round(Date.now() / 1000),
+ kind: 1,
+ content: "hi mom",
+ tags: [],
+ };
+
+ let signEvent = {
+ id: crypto.randomUUID(),
+ method: "sign_event",
+ params: [JSON.stringify(testEvent)],
+ };
+
+ let event = {
+ created_at: Math.round(Date.now() / 1000),
+ kind: 24133,
+ pubkey,
+ content: encrypt(JSON.stringify(signEvent), ck),
+ tags: [["p", pk]],
+ };
+
+ let signedEvent = finalizeEvent(event, sk);
+ await relay.publish(signedEvent);
+ };
+
+ let nostrConnect = async () => {
+ let secret = crypto.randomUUID();
+ let sk = generateSecretKey();
+ console.log("SK", bytesToHex(sk));
+ let pubkey = getPublicKey(sk);
+ console.log("PUBKEY", pubkey);
+ connectUrl = `nostrconnect://${pubkey}?relay=wss%3A%2F%2Frelay.coinos.io&perms=nip44_encrypt%2Cnip44_decrypt%2Csign_event%3A13%2Csign_event%3A14%2Csign_event%3A1059&name=Coinos&secret=${secret}`;
+
+ const relay = await Relay.connect("wss://relay.coinos.io");
+ console.log(`connected to ${relay.url}`);
+ let finished, remoteSignerPubkey;
+ const sub = relay.subscribe([{ kinds: [24133], "#p": [pubkey] }], {
+ async onevent(event) {
+ console.log("got event:", event);
+ remoteSignerPubkey = event.pubkey;
+ // console.log("decrypted", decrypt(event.content, sk));
+ if (!finished) {
+ finished = true;
+ let event = {
+ created_at: Math.round(Date.now() / 1000),
+ kind: 24133,
+ pubkey,
+ content: encrypt(
+ JSON.stringify({
+ id: "1234",
+ method: "get_public_key",
+ params: [],
+ }),
+ sk,
+ hexToBytes(remoteSignerPubkey),
+ ),
+ tags: [["p", remoteSignerPubkey]],
+ };
+
+ console.log("asking for public key", event);
+ console.log("DECRYPTED", decrypt(event.content, sk));
+ let signedEvent = finalizeEvent(event, sk);
+ console.log("sleeping");
+ await new Promise((r) => setTimeout(r, 5000));
+ console.log("sending");
+ await relay.publish(signedEvent);
+ }
+ },
+ });
+ console.log("subbed");
+ };
+
+ let pubkey = $state();
+ let ev = $derived({
+ kind: 1,
+ created_at: Date.now(),
+ content: id,
+ tags: [],
+ });
+
+ let nsec = $state();
+ let showNsec = $state();
+ let toggleNsec = () => (showNsec = !showNsec);
+ let nsecSign = async () => {
+ let sk;
+ if (nsec.startsWith("nsec")) sk = nip19.decode(nsec).data as Uint8Array;
+ else sk = hexToBytes(nsec);
+
+ nostrLogin(finalizeEvent(ev, sk));
+ };
+
+ let extensionSign = async () => {
+ nostrLogin(await window.nostr.signEvent(ev));
+ };
+
+ let signerGetPubkey = async () => {
+ const signer = new AmberClipboardSigner();
+ pubkey = await signer.getPublicKey();
+ ev.pubkey = pubkey;
+ console.log("PUBKEY", ev.pubkey);
+ };
+ //
+ // let signerSign = async () => {
+ // const signer = new AmberClipboardSigner();
+ // ev.sig = await signer.signEvent(ev);
+ // nostrLogin(await signer.signEvent(ev));
+ // };
+ //
+ let signerSign = async () => {
+ document.addEventListener("focus", signerSigned, true);
+ };
+
+ const signerSigned = async () => {
+ document.removeEventListener("focus", signerSigned, true);
+ await new Promise((resolve) => setTimeout(resolve, 500));
+ ev.sig = await navigator.clipboard.readText();
+ ev.id = getEventHash(ev);
+ nostrLogin(ev);
+ };
+
+ let nostrLogin = async (signedEvent) => {
+ // let pubkey = await window.nostr.getPublicKey();
+ const formData = new FormData();
+
+ console.log("SIGNED", signedEvent);
+
+ try {
+ formData.append("loginRedirect", redirect);
+ formData.append("token", token);
+ formData.append("event", JSON.stringify(signedEvent));
+ formData.append("id", id);
+
+ let response = await fetch("/login?/nostr", {
+ method: "POST",
+ body: formData,
+ });
+
+ const result = deserialize(await response.text());
+
+ if (result.type === "success") {
+ await invalidateAll();
+ }
+
+ applyAction(result);
+ } catch (e) {
+ fail(e.message);
+ }
+ };
+</script>
+
+{#if nostrSignin}
+ <div
+ class="fixed inset-0 bg-base-100 bg-opacity-90 overflow-y-auto h-full w-full z-50"
+ >
+ <div
+ class="relative mx-auto mt-10 p-5 border w-96 shadow-lg rounded-md bg-base-100 space-y-5"
+ >
+ <h1 class="text-center text-2xl font-semibold">
+ {$t("login.nostr")}
+ </h1>
+
+ <!-- <div class="break-all"> -->
+ <!-- {JSON.stringify(ev)} -->
+ <!-- </div> -->
+ {#if pubkey}
+ <a
+ href={`nostrsigner:${encodeURIComponent(JSON.stringify(ev))}?compressionType=none&returnType=signature&type=sign_event&appName=Coinos`}
+ type="button"
+ class="btn"
+ onclick={signerSign}
+ >
+ <iconify-icon
+ icon="material-symbols:diamond-rounded"
+ width="32"
+ class="text-orange-400"
+ ></iconify-icon>
+ <div class="my-auto">NIP-55 Signer Step 2</div>
+ </a>
+ {:else}
+ {#if extension}
+ <button type="button" class="btn" onclick={extensionSign}>
+ <iconify-icon
+ icon="lucide-lab:bee"
+ width="32"
+ class="text-yellow-400"
+ ></iconify-icon>
+ <div class="my-auto">NIP-10 Extension</div>
+ </button>
+ {/if}
+ <!-- {#if connectUrl} -->
+ <!-- <a href={connectUrl}> -->
+ <!-- connect -->
+ <!-- <img -->
+ <!-- src={`/qr/${encodeURIComponent(connectUrl)}/raw`} -->
+ <!-- class="z-10 border-4 border-white" -->
+ <!-- alt="QR" -->
+ <!-- /> -->
+ <!-- <div class="break-all"> -->
+ <!-- {connectUrl} -->
+ <!-- </div> -->
+ <!-- </a> -->
+ <!-- <button class="btn" onclick={() => copy(connectUrl)}>Copy</button> -->
+ <!-- {:else} -->
+ <!-- <button type="button" class="btn" onclick={nostrConnect}> -->
+ <!-- <img src="/images/nostr.png" class="w-8" /> -->
+ <!-- <div class="my-auto">NIP-46 Connect</div> -->
+ <!-- </button> -->
+ <!-- {/if} -->
+ {#if signer}
+ <a type="button" class="btn" onclick={signerGetPubkey}>
+ <iconify-icon
+ icon="material-symbols:diamond-rounded"
+ width="32"
+ class="text-orange-400"
+ ></iconify-icon>
+ <div class="my-auto">NIP-55 Signer</div>
+ </a>
+ {/if}
+
+ {#if showNsec}
+ <input bind:value={nsec} placeholder="nsec..." />
+ <button type="button" class="btn" onclick={nsecSign}>
+ <iconify-icon icon="ph:key-bold" width="32"></iconify-icon>
+ <div class="my-auto">Nsec login</div>
+ </button>
+ {:else}
+ <button type="button" class="btn" onclick={toggleNsec}>
+ <iconify-icon icon="ph:key-bold" width="32"></iconify-icon>
+ <div class="my-auto">Nsec login</div>
+ </button>
+ {/if}
+ {/if}
+ </div>
+ </div>
+{/if}
diff --git a/src/locales/en.json b/src/locales/en.json
index 937b3be3..848b467d 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -168,7 +168,8 @@
"signIn": "Sign In",
"signUp": "Sign Up",
"failed": "Login failed",
- "nostr": "Sign in with Nostr"
+ "nostr": "Sign in with Nostr",
+ "nostrConnect": "Nostr connect"
},
"nav": {
"dark": "Dark",
diff --git a/src/routes/(app)/[username]/(profile)/+page.svelte b/src/routes/(app)/[username]/(profile)/+page.svelte
index a498f0f7..6530f149 100644
--- a/src/routes/(app)/[username]/(profile)/+page.svelte
+++ b/src/routes/(app)/[username]/(profile)/+page.svelte
@@ -8,6 +8,7 @@
import { t } from "$lib/translations";
import { installPrompt } from "$lib/store";
import { afterNavigate, preloadData } from "$app/navigation";
+ import { page } from "$app/stores";
let { data } = $props();
@@ -24,7 +25,9 @@
$installPrompt = null;
};
+ let pubkey = $state();
if (user) user.savings = 0;
+ let { host } = $derived($page.url);
</script>
<div class="space-y-2">
@@ -37,14 +40,16 @@
<a href={`/account/savings`} class="contents">
<button class="btn btn-lg w-full rounded-2xl whitespace-nowrap">
- <iconify-icon noobserver icon="ph:plus-circle-bold" width="32"></iconify-icon>
+ <iconify-icon noobserver icon="ph:plus-circle-bold" width="32"
+ ></iconify-icon>
{$t("accounts.addAccount")}
</button>
</a>
{#if $installPrompt}
<button class="btn btn-accent lg:hidden" onclick={install}>
- <iconify-icon noobserver icon="ph:floppy-disk-bold" width="32"></iconify-icon>
+ <iconify-icon noobserver icon="ph:floppy-disk-bold" width="32"
+ ></iconify-icon>
{$t("user.install")}
</button>
{/if}
@@ -62,7 +67,10 @@
<button
class="btn btn-accent !text-2xl items-center !w-full sm:!max-w-[400px] mx-auto"
>
- <iconify-icon noobserver icon="ph:lightning-fill" class="text-yellow-300"
+ <iconify-icon
+ noobserver
+ icon="ph:lightning-fill"
+ class="text-yellow-300"
></iconify-icon>
<div>
{$t("user.pay")}
@@ -72,3 +80,4 @@
</a>
{/if}
</div>
+
diff --git a/src/routes/(app)/events/+server.ts b/src/routes/(app)/events/+server.ts
index 42b9f3dd..59fc472e 100644
--- a/src/routes/(app)/events/+server.ts
+++ b/src/routes/(app)/events/+server.ts
@@ -1,5 +1,11 @@
import { auth, post } from "$lib/utils";
-import { json, error } from "@sveltejs/kit";
+import { error, json } from "@sveltejs/kit";
+
+export async function GET({ url }) {
+ console.log(url.searchParams.get("event"));
+ console.log(url.searchParams.get("uid"));
+ return json({});
+}
export async function POST({ cookies, request }) {
const body = await request.json();
diff --git a/src/routes/(app)/invoice/[id]/paid/+page.svelte b/src/routes/(app)/invoice/[id]/paid/+page.svelte
index a13692d7..bc61d7fe 100644
--- a/src/routes/(app)/invoice/[id]/paid/+page.svelte
+++ b/src/routes/(app)/invoice/[id]/paid/+page.svelte
@@ -2,29 +2,36 @@
import Amount from "$comp/Amount.svelte";
import Success from "$comp/Success.svelte";
import { t } from "$lib/translations";
+ import { loc } from "$lib/utils";
let { data } = $props();
let { amount, currency, rate, received, pending, tip, user, id } = $derived(
data.invoice,
);
+ let locale = $derived(loc(user));
// toast.pop(0);
</script>
<div class="container px-4 text-center mx-auto">
{#if pending}
- <iconify-icon noobserver icon="ph:clock-bold" class="text-warning" width="160"
+ <iconify-icon
+ noobserver
+ icon="ph:clock-bold"
+ class="text-warning"
+ width="160"
></iconify-icon>
<h1 class="text-3xl md:text-4xl font-bold mb-6">Payment detected</h1>
- <Amount amount={pending - tip} {tip} {rate} {currency} />
+ <Amount amount={pending - tip} {tip} {rate} {currency} {locale} />
{:else}
<Success
amount={received - tip}
{rate}
{tip}
{currency}
+ {locale}
title={$t("invoice.paymentSuccessful")}
/>
{/if}
diff --git a/src/routes/(app)/ln/[lnurl]/+page.server.ts b/src/routes/(app)/ln/[lnurl]/+page.server.ts
index 53445461..7b63e6f5 100644
--- a/src/routes/(app)/ln/[lnurl]/+page.server.ts
+++ b/src/routes/(app)/ln/[lnurl]/+page.server.ts
@@ -7,12 +7,10 @@ export async function load({ params, parent }) {
if (!user) redirect(307, "/register");
const rates = await getRates();
- console.log("YO");
let data;
try {
const { lnurl } = params;
data = await get(`/decode?text=${lnurl}`);
- console.log("DATA", data);
} catch (e) {
const { message } = e as Error;
error(500, message);
diff --git a/src/routes/(app)/pay/[username]/[...amount]/+page.server.ts b/src/routes/(app)/pay/[username]/[...amount]/+page.server.ts
index 812b97ec..b0f824ed 100644
--- a/src/routes/(app)/pay/[username]/[...amount]/+page.server.ts
+++ b/src/routes/(app)/pay/[username]/[...amount]/+page.server.ts
@@ -45,11 +45,8 @@ export const actions = {
default: async ({ cookies, request }) => {
let id;
const body = await fd(request);
- console.log("BODY", body);
const { amount, rate, prompt, type, username } = body;
- console.log("AMT", amount);
-
try {
({ id } = await post(
"/invoice",
@@ -69,7 +66,8 @@ export const actions = {
return fail(400, { message });
}
- if (prompt) redirect(307, `/invoice/${id}/tip`);
+ if (parseInt(amount) > 0 && prompt) redirect(307, `/invoice/${id}/tip`);
+ else if (parseInt(amount) <= 0 ) redirect(307, `/${username}/receive`);
redirect(307, `/invoice/${id}`);
},
};
diff --git a/src/routes/(app)/send/invoice/[id]/+page.server.ts b/src/routes/(app)/send/invoice/[id]/+page.server.ts
index 9b971f2f..658629ef 100644
--- a/src/routes/(app)/send/invoice/[id]/+page.server.ts
+++ b/src/routes/(app)/send/invoice/[id]/+page.server.ts
@@ -8,7 +8,7 @@ export async function load({ cookies, params: { id }, parent }) {
const invoice = await get(`/invoice/${id}`);
- if (invoice.prompt && invoice.tip === null)
+ if (invoice.amount && invoice.prompt && invoice.tip === null)
redirect(307, `/invoice/${id}/tip`);
if (invoice.memoPrompt && invoice.memo === null)
@@ -17,7 +17,7 @@ export async function load({ cookies, params: { id }, parent }) {
if (user && invoice.aid === aid)
error(500, { message: "Cannot send to self" });
else if (user && ![types.lightning, types.bolt12].includes(invoice.type))
- redirect(307, `/send/${invoice.type}/${invoice.hash}`);
+ redirect(307, `/send/${invoice.type === types.ecash ? "ecash" : "bitcoin"}/${invoice.hash}`);
if (!user) redirect(307, `/invoice/${id}`);
diff --git a/src/routes/(static)/login/+page.server.ts b/src/routes/(static)/login/+page.server.ts
index 975dab5d..6bf06ad2 100644
--- a/src/routes/(static)/login/+page.server.ts
+++ b/src/routes/(static)/login/+page.server.ts
@@ -1,6 +1,6 @@
+import { PUBLIC_COINOS_URL } from "$env/static/public";
import { fd, get, login } from "$lib/utils";
import { fail, redirect } from "@sveltejs/kit";
-import { PUBLIC_COINOS_URL } from "$env/static/public";
export const load = async ({ parent }) => {
const { user } = await parent();
diff --git a/src/routes/(static)/login/+page.svelte b/src/routes/(static)/login/+page.svelte
index bfd30fe2..dec02b17 100644
--- a/src/routes/(static)/login/+page.svelte
+++ b/src/routes/(static)/login/+page.svelte
@@ -3,21 +3,22 @@
import handler from "$lib/handler";
import { onMount } from "svelte";
- import { applyAction, deserialize } from "$app/forms";
+ import { deserialize } from "$app/forms";
import { tick } from "svelte";
import { fly } from "svelte/transition";
import { enhance } from "$app/forms";
+ import Nostr from "$comp/Nostr.svelte";
import Pinpad from "$comp/Pinpad.svelte";
import PasswordInput from "$comp/PasswordInput.svelte";
- import { focus, fail } from "$lib/utils";
+ import { wait, copy, focus, fail } from "$lib/utils";
import { password, pin, loginRedirect } from "$lib/store";
import { t } from "$lib/translations";
import { page } from "$app/stores";
import { invalidateAll } from "$app/navigation";
- let nostr = $state();
+ let { data, form } = $props();
+
onMount(() => {
- if (browser && window.nostr) nostr = true;
$pin = undefined;
localStorage.clear();
sessionStorage.clear();
@@ -26,11 +27,10 @@
if (lang) document.cookie = `lang=${lang} ;`;
});
- let { data, form } = $props();
let { id } = $derived(data);
+ let nostrSignin = $state();
let token = $state("");
- let code = [];
$password = undefined;
@@ -78,43 +78,6 @@
});
});
- let nostrLogin = async () => {
- let pubkey = await window.nostr.getPublicKey();
- const formData = new FormData();
-
- let ev = {
- kind: 1,
- pubkey,
- created_at: Date.now(),
- content: id,
- tags: [],
- };
-
- try {
- let signedEvent = await window.nostr.signEvent(ev);
-
- formData.append("loginRedirect", redirect);
- formData.append("token", token);
- formData.append("event", JSON.stringify(signedEvent));
- formData.append("id", id);
-
- let response = await fetch("?/nostr", {
- method: "POST",
- body: formData,
- });
-
- const result = deserialize(await response.text());
-
- if (result.type === "success") {
- await invalidateAll();
- }
-
- applyAction(result);
- } catch (e) {
- fail(e.message);
- }
- };
-
let redirect = $derived(
$loginRedirect || $page.url.searchParams.get("redirect"),
);
@@ -163,13 +126,10 @@
{$t("login.signIn")}
</button>
- {#if nostr}
- <button type="button" class="btn" onclick={nostrLogin}>
- <img src="/images/nostr.png" class="w-8" />
- <div class="my-auto">{$t("login.nostr")}</div>
- </button>
- {/if}
-
+ <button type="button" class="btn" onclick={() => (nostrSignin = true)}>
+ <img src="/images/nostr.png" class="w-8" />
+ <div class="my-auto">{$t("login.nostr")}</div>
+ </button>
<p class="text-secondary text-center font-medium">
{$t("login.noAccount")}
<a
@@ -203,3 +163,5 @@
</div>
</div>
{/if}
+
+<Nostr {id} {nostrSignin} {redirect} {token} />
diff --git a/src/routes/amber/[id]/[pubkey]/+server.ts b/src/routes/amber/[id]/[pubkey]/+server.ts
new file mode 100644
index 00000000..6a6f69cf
--- /dev/null
+++ b/src/routes/amber/[id]/[pubkey]/+server.ts
@@ -0,0 +1,6 @@
+import { json } from "@sveltejs/kit";
+
+export async function GET({ params }) {
+ console.log(params);
+ return json({});
+}