diff options
| author | Adam Soltys <[email protected]> | 2026-03-03 22:45:50 -0800 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-03-03 22:45:50 -0800 |
| commit | 1e9ed8fa128ec5ae038be14ef168cad37c1c0eb1 (patch) | |
| tree | 29399a82df3f981a24ef6cc84562c8ee006738d7 /src | |
| parent | e66a08d18f2bef2eccfe84042a2674141a73d1c2 (diff) | |
| download | coinos-ui-1e9ed8fa128ec5ae038be14ef168cad37c1c0eb1.tar.gz coinos-ui-1e9ed8fa128ec5ae038be14ef168cad37c1c0eb1.zip | |
api proxy
Diffstat (limited to 'src')
48 files changed, 160 insertions, 283 deletions
diff --git a/src/components/AppForm.svelte b/src/components/AppForm.svelte index 86338a11..295fd6b6 100644 --- a/src/components/AppForm.svelte +++ b/src/components/AppForm.svelte @@ -57,7 +57,7 @@ let del = async () => { try { - await post(`/post/apps/delete`, { pubkey }); + await post(`/api/apps/delete`, { pubkey }); goto("/settings/nostr"); } catch (e: any) { fail(e.message); diff --git a/src/components/Event.svelte b/src/components/Event.svelte index c08bd452..eef48868 100644 --- a/src/components/Event.svelte +++ b/src/components/Event.svelte @@ -21,11 +21,11 @@ const body = new FormData(); if (!user) return goto(`/register?redirect=${$page.url.pathname}`); - let request = await post("/post/zapRequest", { amount, id }); - let { pr: payreq } = await post("/post/zap", { + let request = await post("/api/zapRequest", { amount, id }); + let { pr: payreq } = await post("/api/zap", { event: await sign(request), }); - await post("/post/payments", { amount, payreq }); + await post("/api/payments", { amount, payreq }); } catch (e: any) { console.log(e); fail(e.message); diff --git a/src/components/Numpad.svelte b/src/components/Numpad.svelte index eee43621..10f5d8f3 100644 --- a/src/components/Numpad.svelte +++ b/src/components/Numpad.svelte @@ -255,12 +255,19 @@ <div class="space-y-5 w-full"> <div class="text-center"> <div class="text-5xl md:text-6xl font-semibold tracking-widest flex justify-center"> - <div class="my-auto" class:text-5xl={!fiat}> - <span class:hidden={!fiat}> + <button + type="button" + class="my-auto cursor-pointer select-none" + class:text-5xl={!fiat} + aria-label="Swap currency display" + onclick={(e) => { e.preventDefault(); e.stopPropagation(); swap(); }} + > + {#if fiat} {#if position === "before"}{symbol}{/if} - </span> - <PhLightningFill width="48" class="text-yellow-300" /> - </div> + {:else} + <PhLightningFill width="48" class="text-yellow-300" /> + {/if} + </button> <!-- contenteditable input --> <div @@ -287,26 +294,15 @@ bind:this={element} ></div> - {#if fiat && position === "after"}{symbol}{/if} + {#if fiat && position === "after"} + <button + type="button" + class="cursor-pointer select-none" + aria-label="Swap currency display" + onclick={(e) => { e.preventDefault(); e.stopPropagation(); swap(); }} + >{symbol}</button> + {/if} </div> - - <!-- swap button --> - <button - type="button" - class="flex items-center justify-center text-2xl cursor-pointer mx-auto select-none" - aria-label="Swap currency display" - onclick={(e) => { - e.preventDefault(); - e.stopPropagation(); - swap(); - }} - > - <span class="flex items-center" class:hidden={!fiat}> - <PhLightningFill width="48" class="text-yellow-300" /> - {s(amount, locale)} - </span> - <span class:hidden={fiat}>{f(amountFiat, currency, locale)}</span> - </button> </div> <div class="grid grid-cols-3 gap-2 w-full mx-auto"> diff --git a/src/components/Password.svelte b/src/components/Password.svelte index ab1b7d9d..7651d5a1 100644 --- a/src/components/Password.svelte +++ b/src/components/Password.svelte @@ -13,7 +13,7 @@ let submit = async (e) => { e.preventDefault(); try { - let res = await post("/password", { password }); + let res = await post("/api/password", { password }); $passwordPrompt = false; $pw = password; } catch (e) { diff --git a/src/components/Pin.svelte b/src/components/Pin.svelte index da0c6aeb..c8ead368 100644 --- a/src/components/Pin.svelte +++ b/src/components/Pin.svelte @@ -27,7 +27,7 @@ if (p?.length !== 6) return; let result; try { - result = await post("/pin", { pin: hashPin(p) }); + result = await post("/api/pin", { pin: hashPin(p) }); } catch (e) { console.log("Pin check failed", e); } diff --git a/src/lib/ark.ts b/src/lib/ark.ts index 18652d1e..e223d013 100644 --- a/src/lib/ark.ts +++ b/src/lib/ark.ts @@ -303,7 +303,7 @@ const _syncTransactions = async (aid: string) => { const bal = await wallet.getBalance(); const balance = (bal.available || 0) + (bal.preconfirmed || 0); - const result = await post("/post/ark/sync", { transactions, aid, balance, arkAddress: walletAddr }); + const result = await post("/api/ark/sync", { transactions, aid, balance, arkAddress: walletAddr }); cacheVaultSnapshot(); // fire-and-forget return result; }; @@ -456,14 +456,14 @@ export const sendArkViaForward = async ({ try { const txid = await sendArk(serverArkAddress, amount); - await post("/post/ark/vault-send", { hash: txid, amount, aid }); + await post("/api/ark/vault-send", { hash: txid, amount, aid }); const invoice: any = { type: "ark", amount, aid }; if (forward) invoice.forward = forward; - const inv = await post("/post/invoice", { invoice, user }); + const inv = await post("/api/invoice", { invoice, user }); - return await post("/post/ark/receive", { amount, hash: txid, iid: inv.id }); + return await post("/api/ark/receive", { amount, hash: txid, iid: inv.id }); } finally { vaultForwarding = false; } diff --git a/src/lib/bitcoinSync.ts b/src/lib/bitcoinSync.ts index 9061e2fc..92d3382b 100644 --- a/src/lib/bitcoinSync.ts +++ b/src/lib/bitcoinSync.ts @@ -6,7 +6,7 @@ export const syncBitcoinVault = async (aid: string) => { if (syncing) return; syncing = true; try { - return await post("/post/bitcoin/sync", { aid }); + return await post("/api/bitcoin/sync", { aid }); } finally { syncing = false; } diff --git a/src/lib/keyWrapping.ts b/src/lib/keyWrapping.ts index b176e741..fb46f767 100644 --- a/src/lib/keyWrapping.ts +++ b/src/lib/keyWrapping.ts @@ -45,7 +45,7 @@ export async function resolveCanonicalKey( export async function saveEncryptedKeys( encryptedKeys: Record<string, string>, ): Promise<void> { - await post("/post/user", { encryptedKeys }); + await post("/api/user", { encryptedKeys }); } export function passwordMethodId(): string { diff --git a/src/lib/nostr.ts b/src/lib/nostr.ts index 1bbf6bcc..4b7316e8 100644 --- a/src/lib/nostr.ts +++ b/src/lib/nostr.ts @@ -136,7 +136,7 @@ export const sign = async (event: any, user?: any) => { }; export const send = (event: EventTemplate) => { - return post("/events", { event }); + return post("/api/event", { event }); }; const getPassword = async (): Promise<string> => { diff --git a/src/routes/(app)/+layout.svelte b/src/routes/(app)/+layout.svelte index f4d036f2..248206fb 100644 --- a/src/routes/(app)/+layout.svelte +++ b/src/routes/(app)/+layout.svelte @@ -104,8 +104,8 @@ try { const { serverArkAddress, amount, iid } = result.forward; const txid = await sendArk(serverArkAddress, amount); - await post("/post/ark/vault-send", { hash: txid, amount, aid }); - await post("/post/ark/receive", { amount, hash: txid, iid }); + await post("/api/ark/vault-send", { hash: txid, amount, aid }); + await post("/api/ark/receive", { amount, hash: txid, iid }); } catch (e) { console.error("Ark custodial forward failed:", e); } finally { diff --git a/src/routes/(app)/[username]/(profile)/+page.svelte b/src/routes/(app)/[username]/(profile)/+page.svelte index 2eb424e1..4ed8eaaf 100644 --- a/src/routes/(app)/[username]/(profile)/+page.svelte +++ b/src/routes/(app)/[username]/(profile)/+page.svelte @@ -46,15 +46,19 @@ const mn = entropyToMnemonic(entropy, wordlist); const seed = await mnemonicToSeed(mn); const master = HDKey.fromMasterSeed(seed, versions); - const child = master.derive("m/84'/0'/0'"); + const existing = await fetch("/api/accounts").then((r) => r.json()); + const nextIdx = existing + .filter((a: any) => a.pubkey && a.fingerprint) + .reduce((max: number, a: any) => Math.max(max, (a.accountIndex ?? 0) + 1), 0); + const child = master.derive(`m/84'/0'/${nextIdx}'`); const pubkey = child.publicExtendedKey; const fingerprint = child.fingerprint.toString(16).padStart(8, "0"); - await post("/account", { + await post("/api/accounts", { fingerprint, pubkey, name: $t("accounts.vault"), type: "bitcoin", - accountIndex: 0, + accountIndex: nextIdx, }); invalidate("app:payments"); } catch (e: any) { diff --git a/src/routes/(app)/account/+server.ts b/src/routes/(app)/account/+server.ts deleted file mode 100644 index 4387373e..00000000 --- a/src/routes/(app)/account/+server.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { auth, get, post } from "$lib/utils"; -import { error, json } from "@sveltejs/kit"; - -export async function GET({ cookies }) { - try { - return json(await get("/accounts", auth(cookies))); - } catch (e: any) { - console.log(e); - error(500, e.message); - } -} - -export async function POST({ cookies, request }) { - try { - const body = await request.json(); - return json(await post("/accounts", body, auth(cookies))); - } catch (e: any) { - console.log(e); - error(500, e.message); - } -} diff --git a/src/routes/(app)/account/[id]/+page.svelte b/src/routes/(app)/account/[id]/+page.svelte index bb0251db..582fe3ec 100644 --- a/src/routes/(app)/account/[id]/+page.svelte +++ b/src/routes/(app)/account/[id]/+page.svelte @@ -111,7 +111,7 @@ let pubkey = child.publicExtendedKey; let fp = child.fingerprint.toString(16).padStart(8, "0"); - await post(`/post/account/${id}`, { + await post(`/api/account/${id}`, { fingerprint: fp, pubkey, seed, @@ -131,7 +131,7 @@ let del = async () => { try { - await post("/post/account/delete", { id }); + await post("/api/account/delete", { id }); goto(`/${user.username}`); } catch (e: any) { fail(e.message); diff --git a/src/routes/(app)/account/ark/+page.svelte b/src/routes/(app)/account/ark/+page.svelte index 3d200070..65d03aad 100644 --- a/src/routes/(app)/account/ark/+page.svelte +++ b/src/routes/(app)/account/ark/+page.svelte @@ -42,7 +42,7 @@ const identity = SingleKey.fromHex(arkHex); const wallet = await Wallet.create({ identity, arkServerUrl }); arkAddress = await wallet.getAddress(); - await post("/account", { name, type, arkAddress }); + await post("/api/accounts", { name, type, arkAddress }); goto(`/${user.username}`); } catch (e: any) { console.log("Ark account creation failed", e); diff --git a/src/routes/(app)/account/new/+page.svelte b/src/routes/(app)/account/new/+page.svelte index da9b4c72..1761f3f6 100644 --- a/src/routes/(app)/account/new/+page.svelte +++ b/src/routes/(app)/account/new/+page.svelte @@ -37,7 +37,7 @@ const wallet = await Wallet.create({ identity, arkServerUrl }); const arkAddress = await wallet.getAddress(); - await post("/account", { name: $t("accounts.savings"), type: "ark", arkAddress }); + await post("/api/accounts", { name: $t("accounts.savings"), type: "ark", arkAddress }); goto(`/${user.username}`); } catch (e: any) { console.log("Ark account creation failed", e); diff --git a/src/routes/(app)/account/pass/+page.svelte b/src/routes/(app)/account/pass/+page.svelte index c7f6c0ab..a76494c0 100644 --- a/src/routes/(app)/account/pass/+page.svelte +++ b/src/routes/(app)/account/pass/+page.svelte @@ -41,15 +41,19 @@ await mnemonicToSeed(mn), versions, ); - const child = master.derive("m/84'/0'/0'"); + const accounts = await fetch("/api/accounts").then((r) => r.json()); + const nextIdx = (accounts || []) + .filter((a: any) => a.pubkey && a.fingerprint && a.type !== "ark") + .reduce((max: number, a: any) => Math.max(max, (a.accountIndex ?? 0) + 1), 0); + const child = master.derive(`m/84'/0'/${nextIdx}'`); const pubkey = child.publicExtendedKey; const fingerprint = child.fingerprint.toString(16).padStart(8, "0"); - await post("/account", { + await post("/api/accounts", { fingerprint, pubkey, name: $t("accounts.vault"), type: "bitcoin", - accountIndex: 0, + accountIndex: nextIdx, }); $mnemonic = ""; diff --git a/src/routes/(app)/account/seed/+page.svelte b/src/routes/(app)/account/seed/+page.svelte index 342852ca..70f3fe32 100644 --- a/src/routes/(app)/account/seed/+page.svelte +++ b/src/routes/(app)/account/seed/+page.svelte @@ -27,15 +27,19 @@ const mn = entropyToMnemonic(entropy, wordlist); const seed = await mnemonicToSeed(mn); const master = HDKey.fromMasterSeed(seed, versions); - const child = master.derive("m/84'/0'/0'"); + const accounts = await fetch("/api/accounts").then((r) => r.json()); + const nextIdx = (accounts || []) + .filter((a: any) => a.pubkey && a.fingerprint && a.type !== "ark") + .reduce((max: number, a: any) => Math.max(max, (a.accountIndex ?? 0) + 1), 0); + const child = master.derive(`m/84'/0'/${nextIdx}'`); const pubkey = child.publicExtendedKey; const fingerprint = child.fingerprint.toString(16).padStart(8, "0"); - await post("/account", { + await post("/api/accounts", { fingerprint, pubkey, name: $t("accounts.vault"), type: "bitcoin", - accountIndex: 0, + accountIndex: nextIdx, }); await goto(`/${user.username}`); } catch (e: any) { diff --git a/src/routes/(app)/post/[...path]/+server.ts b/src/routes/(app)/api/[...path]/+server.ts index 2c3e6d18..bc12494f 100644 --- a/src/routes/(app)/post/[...path]/+server.ts +++ b/src/routes/(app)/api/[...path]/+server.ts @@ -1,20 +1,25 @@ import { PUBLIC_COINOS_URL as base } from "$env/static/public"; import { auth } from "$lib/utils"; -export async function POST({ cookies, fetch, params, request }) { - const { path } = params; - const headers: Record<string, string> = { "Content-Type": "application/json" }; - +function getHeaders(cookies, request) { + const headers: Record<string, string> = {}; const authHeader = request.headers.get("authorization"); if (authHeader) { headers.authorization = authHeader; } else { Object.assign(headers, auth(cookies)); } + return headers; +} - return fetch(`${base}/${path}`, { +export async function GET({ cookies, fetch, params, request }) { + return fetch(`${base}/${params.path}`, { headers: getHeaders(cookies, request) }); +} + +export async function POST({ cookies, fetch, params, request }) { + return fetch(`${base}/${params.path}`, { method: "POST", body: JSON.stringify(await request.json()), - headers, + headers: { "Content-Type": "application/json", ...getHeaders(cookies, request) }, }); } diff --git a/src/routes/(app)/contacts/+server.ts b/src/routes/(app)/contacts/+server.ts deleted file mode 100644 index 2bc5d28e..00000000 --- a/src/routes/(app)/contacts/+server.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { auth, get } from "$lib/utils"; -import { json } from "@sveltejs/kit"; - -export async function GET({ cookies }) { - try { - const contacts = await get("/contacts", auth(cookies)); - return json(contacts); - } catch (e) { - console.log(e); - return json({}); - } -} diff --git a/src/routes/(app)/disable2fa/+server.ts b/src/routes/(app)/disable2fa/+server.ts deleted file mode 100644 index 0d1c4573..00000000 --- a/src/routes/(app)/disable2fa/+server.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { error, json } from "@sveltejs/kit"; -import { auth, post } from "$lib/utils"; - -export async function POST({ cookies, request }) { - try { - const body = await request.json(); - const res = await post("/disable2fa", body, auth(cookies)); - - return json(res); - } catch { - error(500, "Problem disabling 2fa"); - } -} diff --git a/src/routes/(app)/ecash/mint/+server.ts b/src/routes/(app)/ecash/mint/+server.ts deleted file mode 100644 index e933208e..00000000 --- a/src/routes/(app)/ecash/mint/+server.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { auth, post } from "$lib/utils"; -import { json } from "@sveltejs/kit"; - -export async function POST({ cookies, request }) { - const result = await post("/mint", await request.json(), auth(cookies)); - return json(result); -} diff --git a/src/routes/(app)/email/+server.ts b/src/routes/(app)/email/+server.ts deleted file mode 100644 index 437c0ef0..00000000 --- a/src/routes/(app)/email/+server.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { auth, post } from "$lib/utils"; -import { json } from "@sveltejs/kit"; - -export async function POST({ cookies, request }) { - const result = await post("/request", await request.json(), auth(cookies)); - return json(result); -} diff --git a/src/routes/(app)/enable2fa/+server.ts b/src/routes/(app)/enable2fa/+server.ts deleted file mode 100644 index 76737968..00000000 --- a/src/routes/(app)/enable2fa/+server.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { auth, post } from "$lib/utils"; - -export async function POST({ cookies, request }) { - const body = await request.json(); - const res = await post("/2fa", body, auth(cookies)); - - return new Response(JSON.stringify(res)); -} diff --git a/src/routes/(app)/events/+server.ts b/src/routes/(app)/events/+server.ts deleted file mode 100644 index e2553753..00000000 --- a/src/routes/(app)/events/+server.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { auth, post } from "$lib/utils"; -import { error, json } from "@sveltejs/kit"; - -export async function POST({ cookies, request }) { - const body = await request.json(); - try { - const result = await post("/event", body, auth(cookies)); - return json(result); - } catch (e: any) { - error(500, e.message); - } -} diff --git a/src/routes/(app)/firehose/+page.svelte b/src/routes/(app)/firehose/+page.svelte index b2a2cab4..2a5e8fa6 100644 --- a/src/routes/(app)/firehose/+page.svelte +++ b/src/routes/(app)/firehose/+page.svelte @@ -23,7 +23,7 @@ const subscription = relay.subscribe([{ kinds: [1], limit: 20 }], { async onevent(event: any) { if (paused) return; - const { parts, names } = await post("/post/parseEvent", { event }); + const { parts, names } = await post("/api/parseEvent", { event }); event.author = await get(`/api/users/${event.pubkey}`); event.parts = parts; event.names = names; diff --git a/src/routes/(app)/fund/[id]/access/+page.svelte b/src/routes/(app)/fund/[id]/access/+page.svelte index d2d10ff0..3c9f6410 100644 --- a/src/routes/(app)/fund/[id]/access/+page.svelte +++ b/src/routes/(app)/fund/[id]/access/+page.svelte @@ -15,7 +15,7 @@ let del = async (e, user) => { e.preventDefault(); try { - m = await post(`/post/fund/${id}/managers/delete`, { + m = await post(`/api/fund/${id}/managers/delete`, { id: user.id, }); } catch (e: any) { diff --git a/src/routes/(app)/fund/[id]/withdraw/+server.ts b/src/routes/(app)/fund/[id]/withdraw/+server.ts deleted file mode 100644 index 96d66413..00000000 --- a/src/routes/(app)/fund/[id]/withdraw/+server.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { json } from "@sveltejs/kit"; -import { auth, post } from "$lib/utils"; - -export async function POST({ cookies, request }) { - return json(await post("/take", await request.json(), auth(cookies))); -} diff --git a/src/routes/(app)/invoice/+server.ts b/src/routes/(app)/invoice/+server.ts deleted file mode 100644 index b18eae15..00000000 --- a/src/routes/(app)/invoice/+server.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { auth, post } from "$lib/utils"; -import { error, json } from "@sveltejs/kit"; - -export const POST = async ({ cookies, request }) => { - try { - const body = await request.json(); - return json(await post("/invoice", body, auth(cookies))); - } catch (e) { - const { message } = e as Error; - error(500, message); - } -}; diff --git a/src/routes/(app)/password/+server.ts b/src/routes/(app)/password/+server.ts deleted file mode 100644 index 250dcfed..00000000 --- a/src/routes/(app)/password/+server.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { json } from "@sveltejs/kit"; -import { auth, post } from "$lib/utils"; - -export async function POST({ cookies, request }) { - const result = await post("/password", await request.json(), auth(cookies)); - return json(result); -} diff --git a/src/routes/(app)/payment/[id]/+page.svelte b/src/routes/(app)/payment/[id]/+page.svelte index 78ae22c3..15f42a75 100644 --- a/src/routes/(app)/payment/[id]/+page.svelte +++ b/src/routes/(app)/payment/[id]/+page.svelte @@ -146,7 +146,11 @@ <div class="flex items-center gap-2 min-w-0"> {#if !expanded.has(label)} <span class="text-right break-all"> - {value.slice(0, 8)}...{value.slice(-8)} + {#if value.length > 20} + {value.slice(0, 8)}...{value.slice(-8)} + {:else} + {value} + {/if} </span> {/if} <button @@ -344,6 +348,10 @@ {@render copyable("Txid", txid, `${expl}/tx/${txid}${vout ? "#vout=" + vout : ""}`)} {/if} + {#if (type === "bitcoin" || type === "liquid") && amount > 0 && hash} + {@render copyable("Address", hash, `${expl}/address/${hash}`)} + {/if} + {#if path} {@render copyable("Path", path)} {/if} diff --git a/src/routes/(app)/payment/[id]/print/+server.ts b/src/routes/(app)/payment/[id]/print/+server.ts deleted file mode 100644 index 19f8941e..00000000 --- a/src/routes/(app)/payment/[id]/print/+server.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { auth, post } from "$lib/utils"; -import { json } from "@sveltejs/kit"; - -export async function POST({ cookies, request }) { - const body = await request.json(); - return json(post("/print", body, auth(cookies))); -} diff --git a/src/routes/(app)/pin/+server.ts b/src/routes/(app)/pin/+server.ts deleted file mode 100644 index b24770d5..00000000 --- a/src/routes/(app)/pin/+server.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { json } from "@sveltejs/kit"; -import { auth, post } from "$lib/utils"; - -export async function POST({ cookies, request }) { - const result = await post("/pin", await request.json(), auth(cookies)); - return json(result); -} diff --git a/src/routes/(app)/reset/+server.ts b/src/routes/(app)/reset/+server.ts deleted file mode 100644 index 4b6692d5..00000000 --- a/src/routes/(app)/reset/+server.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { json } from "@sveltejs/kit"; -import { auth, post } from "$lib/utils"; - -export async function POST({ cookies, request }) { - const result = await post("/reset", await request.json(), auth(cookies)); - return json(result); -} diff --git a/src/routes/(app)/send/[...text]/+page.svelte b/src/routes/(app)/send/[...text]/+page.svelte index 658864ba..49a3853e 100644 --- a/src/routes/(app)/send/[...text]/+page.svelte +++ b/src/routes/(app)/send/[...text]/+page.svelte @@ -27,7 +27,7 @@ let loaded = $state(); let loadMore = async () => { loaded = true; - all = await get("/contacts"); + all = await fetch("/api/contacts").then((r) => r.json()); }; let el: any = $state(), @@ -48,12 +48,12 @@ e.stopPropagation(); if (pinned) { - await post("/post/pins/delete", { id }); + await post("/api/pins/delete", { id }); } else { - await post("/post/pins", { id }); + await post("/api/pins", { id }); } - if (all) all = await get("/contacts"); + if (all) all = await fetch("/api/contacts").then((r) => r.json()); invalidate("app:contacts"); }; @@ -62,12 +62,12 @@ e.stopPropagation(); if (trusted) { - await post("/post/trust/delete", { id }); + await post("/api/trust/delete", { id }); } else { - await post("/post/trust", { id }); + await post("/api/trust", { id }); } - if (all) all = await get("/contacts"); + if (all) all = await fetch("/api/contacts").then((r) => r.json()); invalidate("app:contacts"); }; diff --git a/src/routes/(app)/send/ark/[address]/[amount]/[...feeRate]/+page.svelte b/src/routes/(app)/send/ark/[address]/[amount]/[...feeRate]/+page.svelte index 7098628b..bb3229b9 100644 --- a/src/routes/(app)/send/ark/[address]/[amount]/[...feeRate]/+page.svelte +++ b/src/routes/(app)/send/ark/[address]/[amount]/[...feeRate]/+page.svelte @@ -35,7 +35,7 @@ }); } else { const txid = await sendArk(address, parseInt(amount)); - p = await post("/post/ark/vault-send", { + p = await post("/api/ark/vault-send", { hash: txid, amount: parseInt(amount), aid: account.id, diff --git a/src/routes/(app)/send/bitcoin/[address]/[amount]/[...feeRate]/+page.svelte b/src/routes/(app)/send/bitcoin/[address]/[amount]/[...feeRate]/+page.svelte index ed59b14b..d5a8f6e5 100644 --- a/src/routes/(app)/send/bitcoin/[address]/[amount]/[...feeRate]/+page.svelte +++ b/src/routes/(app)/send/bitcoin/[address]/[amount]/[...feeRate]/+page.svelte @@ -5,7 +5,7 @@ import Toggle from "$comp/Toggle.svelte"; import Spinner from "$comp/Spinner.svelte"; import { page } from "$app/stores"; - import { toFiat, f, fail, focus, loc, s, sat, closest, network, versions } from "$lib/utils"; + import { toFiat, f, fail, focus, loc, s, sat, network, versions } from "$lib/utils"; import { pin } from "$lib/store"; import { goto, invalidate } from "$app/navigation"; import { rate } from "$lib/store"; @@ -17,6 +17,7 @@ import { prfDecrypt, isPrfEncrypted } from "$lib/crypto"; import Amount from "$comp/Amount.svelte"; + import Slider from "$comp/Slider.svelte"; let { data, form }: any = $props(); @@ -141,12 +142,16 @@ let { account, amount, address, message, fee, fees, subtract, ourfee, hex, inputs, forward, bumpReserve } = $derived(data); - let feeRate: any = $state((() => data.feeRate)()); + let feeRate: any = $state((() => { + if (data.feeRate) return data.feeRate; + if (data.fees) return data.fees.halfHourFee; + return 1; + })()); $effect(() => { if (fees) { delete fees.minimumFee; - feeRate = feeRate ? closest(Object.values(fees), feeRate) : fees.halfHourFee; + feeLoading = false; } }); @@ -161,29 +166,46 @@ submit: any = $state(), showSettings = $state(); - let presets = $derived(fees ? [ - { label: $t("payments.low"), rate: 0.1 }, - { label: $t("payments.medium"), rate: fees.hourFee }, - { label: $t("payments.high"), rate: fees.fastestFee }, - ] : []); + let lowRate = $derived(fees ? 0.1 : 0.1); + let midRate = $derived(fees ? fees.hourFee : 1); + let highRate = $derived(fees ? Math.max(fees.fastestFee, 8) : 10); + let maxRate = $derived(Math.max(highRate * 2, 16)); - let toggleSettings = () => (showSettings = !showSettings); + let sliderValue = $state(50); - let setFee = () => goto(`/send/bitcoin/${address}/${amount}/${feeRate}`); - let goBack = () => goto(`/send/bitcoin/${address}`); + // Map slider (0-100) to fee rate: 0=low, 50=mid, 100=max + let sliderToRate = (v: number) => { + if (v <= 50) return lowRate + (midRate - lowRate) * (v / 50); + return midRate + (maxRate - midRate) * ((v - 50) / 50); + }; - let pickRate = (r: number) => { - feeRate = r; - setFee(); + let rateToSlider = (r: number) => { + if (r <= midRate) return Math.round(((r - lowRate) / (midRate - lowRate)) * 50); + return Math.round(50 + ((r - midRate) / (maxRate - midRate)) * 50); }; - let applyCustom = () => { - const r = parseFloat(String(feeRate)); - if (r >= 0.1) { - feeRate = r; - setFee(); + // Init slider from feeRate + $effect(() => { + if (fees) { + delete fees.minimumFee; + sliderValue = rateToSlider(feeRate); } + }); + + let setFee = () => goto(`/send/bitcoin/${address}/${amount}/${feeRate}`); + + let slideTimer: ReturnType<typeof setTimeout> | undefined; + let feeLoading = $state(false); + let handleSlide = () => { + const r = Math.round(sliderToRate(sliderValue) * 10) / 10; + feeRate = Math.max(0.1, r); + feeLoading = true; + clearTimeout(slideTimer); + slideTimer = setTimeout(setFee, 500); }; + + let midSlider = $derived(rateToSlider(midRate)); + let highSlider = $derived(rateToSlider(highRate)); </script> <div class="container px-4 max-w-xl mx-auto space-y-5 text-center no-transition"> @@ -206,38 +228,24 @@ <Amount amount={subtract ? amount - fee - ourfee : amount} rate={$rate} {currency} {locale} /> {#if fees} - <div class="text-center"> - <h2 class="text-secondary text-lg">{$t("payments.priority")}</h2> - <div class="flex gap-2 justify-center mb-2"> - {#each presets as preset} - <button - type="button" - class="btn btn-sm !w-auto" - class:btn-accent={feeRate == preset.rate} - onclick={() => pickRate(preset.rate)} - > - {preset.label} - </button> - {/each} - </div> + <div class="text-center space-y-2"> + <h2 class="text-secondary text-lg">{$t("payments.networkFee")}</h2> - <div class="flex items-center justify-center gap-2 mb-2"> - <input - type="number" - min="0.1" - step="0.1" - class="input input-bordered w-24 text-center" - value={feeRate} - oninput={(e) => { - const r = parseFloat(e.currentTarget.value); - if (r >= 0.1) { feeRate = r; setFee(); } - }} - onkeydown={(e) => e.key === 'Enter' && applyCustom()} - /> - <span class="text-secondary text-sm">sat/vB</span> + <div class="px-2"> + <Slider bind:value={sliderValue} handle={handleSlide} min={0} max={100} /> + <div class="flex justify-between text-secondary mt-1 px-1"> + <span>{lowRate} sat/vB</span> + <span>{midRate} sat/vB</span> + <span>{highRate} sat/vB</span> + </div> </div> - <Amount amount={fee} rate={$rate} {currency} {locale} /> + <div class="text-lg">{feeRate} sat/vB</div> + {#if feeLoading} + <Spinner class="text-secondary" /> + {:else} + <Amount amount={fee} rate={$rate} {currency} {locale} /> + {/if} </div> {#if bumpReserve > 0} diff --git a/src/routes/(app)/send/invoice/[id]/+page.svelte b/src/routes/(app)/send/invoice/[id]/+page.svelte index d945ec19..a0af8482 100644 --- a/src/routes/(app)/send/invoice/[id]/+page.svelte +++ b/src/routes/(app)/send/invoice/[id]/+page.svelte @@ -70,9 +70,9 @@ e.stopPropagation(); if (trusted) { - await post("/post/trust/delete", { id: recipient.id }); + await post("/api/trust/delete", { id: recipient.id }); } else { - await post("/post/trust", { id: recipient.id }); + await post("/api/trust", { id: recipient.id }); } invalidate("app:trust"); diff --git a/src/routes/(app)/send/lightning/[payreq]/[...memo]/+page.svelte b/src/routes/(app)/send/lightning/[payreq]/[...memo]/+page.svelte index fc80e161..5669ee1e 100644 --- a/src/routes/(app)/send/lightning/[payreq]/[...memo]/+page.svelte +++ b/src/routes/(app)/send/lightning/[payreq]/[...memo]/+page.svelte @@ -70,7 +70,7 @@ (async () => { try { - const inv = await post("/post/invoice", { + const inv = await post("/api/invoice", { invoice: { type: "bitcoin", amount: parseInt(amount), diff --git a/src/routes/(app)/send/zap/[id]/[...amount]/+page.svelte b/src/routes/(app)/send/zap/[id]/[...amount]/+page.svelte index 62329d7a..21090d23 100644 --- a/src/routes/(app)/send/zap/[id]/[...amount]/+page.svelte +++ b/src/routes/(app)/send/zap/[id]/[...amount]/+page.svelte @@ -29,8 +29,8 @@ try { loading = false; event = await sign(request); - let { pr: payreq } = await post("/post/zap", { event }); - await post("/post/payments", { amount, payreq, pin: $pin }); + let { pr: payreq } = await post("/api/zap", { event }); + await post("/api/payments", { amount, payreq, pin: $pin }); goto(`/e/${id}`); } catch (e: any) { console.log(e); diff --git a/src/routes/(app)/settings/+layout.svelte b/src/routes/(app)/settings/+layout.svelte index 8a4b36a2..2b3de9f5 100644 --- a/src/routes/(app)/settings/+layout.svelte +++ b/src/routes/(app)/settings/+layout.svelte @@ -195,7 +195,7 @@ user.verified = false; - await post("/email", { email }); + await post("/api/request", { email }); warning($t("user.settings.verifying"), false); } catch (e: any) { diff --git a/src/routes/(app)/settings/account/+page.svelte b/src/routes/(app)/settings/account/+page.svelte index 115f46c5..fb8a9538 100644 --- a/src/routes/(app)/settings/account/+page.svelte +++ b/src/routes/(app)/settings/account/+page.svelte @@ -58,7 +58,7 @@ } if (subscription) { - await post("/subscription", { subscription }); + await post("/api/subscription", { subscription }); } }; run(() => { diff --git a/src/routes/(app)/settings/security/+page.svelte b/src/routes/(app)/settings/security/+page.svelte index dbd73b1b..6ba91240 100644 --- a/src/routes/(app)/settings/security/+page.svelte +++ b/src/routes/(app)/settings/security/+page.svelte @@ -136,7 +136,7 @@ let enable2fa = async (twoFa?: any) => { try { if (setting2fa && token && token.length === 6) { - await post("/enable2fa", { token }); + await post("/api/2fa", { token }); success("2FA enabled"); user.twofa = 1; cancel(); @@ -150,7 +150,7 @@ let disable2fa = async (_?: any) => { try { if (disabling2fa && token && token.length === 6) { - await post("/disable2fa", { token }); + await post("/api/disable2fa", { token }); success("2FA disabled"); delete user.twofa; disabling2fa = false; diff --git a/src/routes/(app)/subscription/+server.ts b/src/routes/(app)/subscription/+server.ts deleted file mode 100644 index f25db99a..00000000 --- a/src/routes/(app)/subscription/+server.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { auth, post } from "$lib/utils"; -import { json } from "@sveltejs/kit"; - -export async function POST({ cookies, request }) { - const body = await request.json(); - const result = await post("/subscription", body, auth(cookies)); - return json(result); -} diff --git a/src/routes/(app)/subscription/delete/+server.ts b/src/routes/(app)/subscription/delete/+server.ts deleted file mode 100644 index 1e6c1fdd..00000000 --- a/src/routes/(app)/subscription/delete/+server.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { auth, post } from "$lib/utils"; -import { json } from "@sveltejs/kit"; - -export async function POST({ cookies, request }) { - const body = await request.json(); - const result = await post("/subscription/delete", body, auth(cookies)); - return json(result); -} diff --git a/src/routes/(app)/tag/[tag]/+page.svelte b/src/routes/(app)/tag/[tag]/+page.svelte index f2e50c6c..6446c265 100644 --- a/src/routes/(app)/tag/[tag]/+page.svelte +++ b/src/routes/(app)/tag/[tag]/+page.svelte @@ -27,7 +27,7 @@ async onevent(event: any) { if (paused) return; if (events.find((e) => e.id === event.id)) return; - const { parts, names } = await post("/post/parseEvent", { event }); + const { parts, names } = await post("/api/parseEvent", { event }); event.author = await get(`/api/users/${event.pubkey}`); event.parts = parts; event.names = names; diff --git a/src/routes/(app)/withdraw/+server.ts b/src/routes/(app)/withdraw/+server.ts deleted file mode 100644 index f8e093ef..00000000 --- a/src/routes/(app)/withdraw/+server.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { auth, post } from "$lib/utils"; - -export async function POST({ cookies, request }) { - const body = await request.json(); - await post("/payments", body, auth(cookies)); - - return new Response(JSON.stringify("ok"), { - headers: { "content-type": "application/json" }, - }); -} diff --git a/src/routes/(static)/register/auth/+page.svelte b/src/routes/(static)/register/auth/+page.svelte index aca1f30c..c45d63f6 100644 --- a/src/routes/(static)/register/auth/+page.svelte +++ b/src/routes/(static)/register/auth/+page.svelte @@ -174,7 +174,7 @@ console.log("Nostr key derivation failed", e); } - await post("/post/user", nostrPubkey ? { encryptedKeys, pubkey: nostrPubkey } : { encryptedKeys }); + await post("/api/user", nostrPubkey ? { encryptedKeys, pubkey: nostrPubkey } : { encryptedKeys }); } catch (e) { console.log("Canonical key generation failed", e); } @@ -269,7 +269,7 @@ // Clean up the account created by passkeyCreate if passkey registration failed if (passkeyToken) { try { - await fetch("/post/user/delete", { + await fetch("/api/user/delete", { method: "POST", headers: { "Content-Type": "application/json", diff --git a/src/routes/(static)/support/+page.svelte b/src/routes/(static)/support/+page.svelte index 3a9b011f..6af80f86 100644 --- a/src/routes/(static)/support/+page.svelte +++ b/src/routes/(static)/support/+page.svelte @@ -31,7 +31,7 @@ let submit = (e: any) => { e.preventDefault(); if (isTor) { - post("/post/email", { + post("/api/email", { username: user?.username || `${username} (unverified)`, email, message, @@ -43,7 +43,7 @@ } grecaptcha.ready(() => { grecaptcha.execute(recaptchaSiteKey, { action: "submit" }).then((token) => - post("/post/email", { + post("/api/email", { username: user?.username || `${username} (unverified)`, email, message, |
