diff options
| author | Adam Soltys <[email protected]> | 2026-02-06 22:36:03 -0800 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-02-06 22:36:03 -0800 |
| commit | 3a27ae85e5167c0745a28061d03c7cd9c4f52230 (patch) | |
| tree | a6e8838a607ff9f818953e8737b58a7171942bb0 | |
| parent | e03bc9bd0d7360899c5649caca6dc832181ac0dc (diff) | |
| download | coinos-ui-3a27ae85e5167c0745a28061d03c7cd9c4f52230.tar.gz coinos-ui-3a27ae85e5167c0745a28061d03c7cd9c4f52230.zip | |
pass captcha when sweeping
| -rw-r--r-- | src/routes/(app)/fund/[id]/sweep/[...amount]/+page.server.ts | 74 | ||||
| -rw-r--r-- | src/routes/(app)/fund/[id]/sweep/[...amount]/+page.svelte | 118 |
2 files changed, 146 insertions, 46 deletions
diff --git a/src/routes/(app)/fund/[id]/sweep/[...amount]/+page.server.ts b/src/routes/(app)/fund/[id]/sweep/[...amount]/+page.server.ts index 78df3461..13ce5e9d 100644 --- a/src/routes/(app)/fund/[id]/sweep/[...amount]/+page.server.ts +++ b/src/routes/(app)/fund/[id]/sweep/[...amount]/+page.server.ts @@ -1,9 +1,9 @@ import { randomName, randomPassword } from "$lib/random"; import getRates from "$lib/rates"; -import { auth, get, post, register, sats } from "$lib/utils"; +import { auth, get, login, post, sats } from "$lib/utils"; import { error, redirect } from "@sveltejs/kit"; -export async function load({ cookies, request, params, parent }) { +export async function load({ cookies, params, parent }) { let { user } = await parent(); const { id } = params; let [amount, currency] = params.amount.split("/"); @@ -21,31 +21,59 @@ export async function load({ cookies, request, params, parent }) { amount = Math.round((amount * sats) / rate); } - const username = randomName() + Math.floor(Math.random() * 99) + 1; - const password = randomPassword(); - if (!amount) redirect(307, `/fund/${id}`); - if (!user) { + + if (user) { + try { + ({ amount } = await post("/take", { amount, id }, auth(cookies))); + } catch (e) { + redirect(307, `/fund/${id}`); + } + + return { amount, currency, id, rate, rates, password: null }; + } + + return { amount, currency, id, rate, rates, needsCaptcha: true }; +} + +export const actions = { + default: async ({ cookies, request, params }) => { + const { id } = params; + const form = await request.formData(); + const recaptcha = form.get("recaptcha") as string; + const amount = Number(form.get("amount")); const ip = request.headers.get("cf-connecting-ip"); + const host = request.headers.get("host"); - user = { - username, - password, - fresh: true, - }; + const username = randomName() + Math.floor(Math.random() * 99) + 1; + const password = randomPassword(); - let redirectUrl = `/fund/${id}/sweep`; - if (params.amount && !params.currency) redirectUrl += `/${params.amount}`; - if (params.currency) redirectUrl += `/${params.amount}/params.currency}`; + const user = { username, password, fresh: true, recaptcha }; - await register(user, ip, cookies, redirectUrl); - } + const { sk } = await post( + "/register", + { user }, + { "cf-connecting-ip": ip }, + ); - try { - ({ amount } = await post("/take", { amount, id }, auth(cookies))); - } catch (e) { - redirect(307, `/fund/${id}`); - } + await login(user, cookies, ip, host); - return { amount, currency, id, rate, rates, password }; -} + const expires = new Date(); + expires.setSeconds(expires.getSeconds() + 21000000); + cookies.set("sk", sk, { + path: "/", + expires, + httpOnly: false, + sameSite: "lax", + }); + + let swept; + try { + swept = await post("/take", { amount, id }, auth(cookies)); + } catch (e) { + redirect(307, `/fund/${id}`); + } + + return { amount: swept.amount, password, username }; + }, +}; diff --git a/src/routes/(app)/fund/[id]/sweep/[...amount]/+page.svelte b/src/routes/(app)/fund/[id]/sweep/[...amount]/+page.svelte index 7a7d5402..fbe8e6ca 100644 --- a/src/routes/(app)/fund/[id]/sweep/[...amount]/+page.svelte +++ b/src/routes/(app)/fund/[id]/sweep/[...amount]/+page.svelte @@ -1,36 +1,108 @@ <script> - import { onMount } from "svelte"; + import { PUBLIC_RECAPTCHA_SITE_KEY } from "$env/static/public"; + import { onDestroy, onMount, tick } from "svelte"; + import { browser } from "$app/environment"; + import { enhance } from "$app/forms"; import { password } from "$lib/store"; - import Amount from "$comp/Amount.svelte"; import Success from "$comp/Success.svelte"; - import { scale } from "svelte/transition"; - import { toFiat, f, s } from "$lib/utils"; - import Icon from "$comp/Icon.svelte"; + import Spinner from "$comp/Spinner.svelte"; import { t } from "$lib/translations"; import { loc } from "$lib/utils"; let { data } = $props(); - - let { amount, currency, id, user, rate, rates, password: pw } = data; + let { amount, currency, id, user, rate, rates, needsCaptcha } = $derived(data); let locale = $derived(loc(user)); - onMount(() => ($password = pw)); + let recaptchaToken = $state(""); + let formEl = $state(); + let loading = $state(!!needsCaptcha); + let swept = $state(!needsCaptcha); + let sweptAmount = $state(amount); + let sweptUsername = $state(user?.username); + let recaptchaSiteKey = PUBLIC_RECAPTCHA_SITE_KEY; + let isTor = browser && location.hostname.endsWith(".onion"); + + function handleEnhance() { + return async ({ result }) => { + if (result.type === "success") { + sweptAmount = result.data.amount; + sweptUsername = result.data.username; + $password = result.data.password; + loading = false; + swept = true; + } + }; + } + + onMount(() => { + if (!needsCaptcha) { + $password = data.password; + return; + } + + if (isTor || !recaptchaSiteKey) { + recaptchaToken = ""; + tick().then(() => formEl?.requestSubmit()); + return; + } + + let s = document.createElement("script"); + s.src = + "https://www.google.com/recaptcha/api.js?render=" + recaptchaSiteKey; + s.onload = () => { + grecaptcha.ready(() => { + grecaptcha + .execute(recaptchaSiteKey, { action: "register" }) + .then((token) => { + recaptchaToken = token; + tick().then(() => formEl?.requestSubmit()); + }); + }); + }; + document.head.appendChild(s); + }); + + onDestroy(() => { + if (!browser) return; + const nodeBadge = document.querySelector(".grecaptcha-badge"); + if (nodeBadge) { + document.body.removeChild(nodeBadge.parentNode); + } + const scriptSelector = + "script[src='https://www.google.com/recaptcha/api.js?render=" + + recaptchaSiteKey + + "']"; + const script = document.querySelector(scriptSelector); + if (script) { + script.remove(); + } + }); </script> -<div class="container px-4 text-center mx-auto"> - <Success - {amount} - {rate} - {currency} - {locale} - title={$t("payments.received")} - /> -</div> +{#if loading} + <div class="flex justify-center items-center h-64"> + <Spinner /> + </div> + <form method="POST" class="hidden" bind:this={formEl} use:enhance={handleEnhance}> + <input type="hidden" name="recaptcha" value={recaptchaToken} /> + <input type="hidden" name="amount" value={amount} /> + </form> +{:else if swept} + <div class="container px-4 text-center mx-auto"> + <Success + amount={sweptAmount} + {rate} + {currency} + {locale} + title={$t("payments.received")} + /> + </div> -<a href={`/${user.username}`} aria-label="Continue"> - <div class="opacity-0 w-screen h-screen fixed top-24 left-0 z-50"></div> -</a> + <a href={`/${sweptUsername}`} aria-label="Continue"> + <div class="opacity-0 w-screen h-screen fixed top-24 left-0 z-50"></div> + </a> -<div class="flex justify-center mt-4"> - {$t("payments.tapAnywhere")} -</div> + <div class="flex justify-center mt-4"> + {$t("payments.tapAnywhere")} + </div> +{/if} |
