diff --git a/src/routes/api/limit/+server.ts b/src/routes/api/limit/+server.ts new file mode 100644 index 0000000..a06013e --- /dev/null +++ b/src/routes/api/limit/+server.ts @@ -0,0 +1,7 @@ +import { RateLimiter, json } from "$lib/server/util/index.js"; + +export function POST({ request, getClientAddress }) { + return json({ + success: true, + }); +} diff --git a/src/routes/api/user/+server.ts b/src/routes/api/user/+server.ts index 16b2895..ec8a592 100644 --- a/src/routes/api/user/+server.ts +++ b/src/routes/api/user/+server.ts @@ -2,7 +2,7 @@ import { userRepo } from "$lib/server/repo"; import type { SuyuUser } from "$lib/server/schema"; -import { RateLimiter, json } from "$lib/server/util"; +import { json } from "$lib/server/util"; import { useAuth } from "$lib/util/api"; import type { CreateAccountRequest, @@ -18,8 +18,6 @@ import { HCAPTCHA_KEY } from "$env/static/private"; import validator from "validator"; import bcrypt from "bcrypt"; -const rateLimit = new RateLimiter(); - const randomBytes = promisify(crypto.randomBytes); async function genKey(username: string) { @@ -35,12 +33,6 @@ async function genKey(username: string) { } export async function POST({ request, getClientAddress }) { - if (rateLimit.isLimited(getClientAddress())) { - return json({ - success: false, - error: "rate limited", - }); - } const body: CreateAccountRequest = await request.json(); if (!body.username || !body.email || !body.captchaToken || !body.password) { return json({ @@ -111,11 +103,6 @@ export async function POST({ request, getClientAddress }) { } export async function GET({ request, getClientAddress }) { - if (rateLimit.isLimited(getClientAddress())) - return json({ - success: false, - error: "rate limited", - }); const user = await useAuth(request); if (!user) { return json({ @@ -130,11 +117,6 @@ export async function GET({ request, getClientAddress }) { } export async function DELETE({ request, getClientAddress }) { - if (rateLimit.isLimited(getClientAddress())) - return json({ - success: false, - error: "rate limited", - }); const user = await useAuth(request); if (!user) { return json({ diff --git a/src/routes/api/user/login/+server.ts b/src/routes/api/user/login/+server.ts index 2597440..94fd71c 100644 --- a/src/routes/api/user/login/+server.ts +++ b/src/routes/api/user/login/+server.ts @@ -1,7 +1,10 @@ +import { HCAPTCHA_KEY } from "$env/static/private"; +import { PUBLIC_SITE_KEY } from "$env/static/public"; import { userRepo } from "$lib/server/repo"; -import { RateLimiter, json } from "$lib/server/util/index.js"; +import { json } from "$lib/server/util/index.js"; import type { LoginResponse, LoginRequest } from "$types/api"; import bcrypt from "bcrypt"; +import { verify } from "hcaptcha"; export async function POST({ request, getClientAddress }) { const body: LoginRequest = await request.json(); @@ -11,12 +14,20 @@ export async function POST({ request, getClientAddress }) { body.email.trim() === "" || body.password.trim() === "" || body.email.length > 320 || - body.password.length > 320 + body.password.length > 320 || + !body.captchaToken ) return json({ success: false, error: "missing fields", }); + const res = await verify(HCAPTCHA_KEY, body.captchaToken, getClientAddress(), PUBLIC_SITE_KEY); + if (!res.success) { + return json({ + success: false, + error: "invalid captcha", + }); + } const user = await userRepo.findOne({ where: { email: body.email, diff --git a/src/routes/login/+page.svelte b/src/routes/login/+page.svelte index bca6ae4..a2f4cae 100644 --- a/src/routes/login/+page.svelte +++ b/src/routes/login/+page.svelte @@ -13,16 +13,20 @@ let emailInput = ""; let passwordInput = ""; - $: disabled = !emailInput || !passwordInput; + let captchaToken = ""; + $: disabled = !emailInput || !passwordInput || !captchaToken; export let data: PageData; if (Object.keys(data.user).length !== 0 && browser) goto("/account"); + let shouldLoadCaptcha = false; + async function logIn() { const res = await SuyuAPI.users.login({ email: emailInput, password: passwordInput, + captchaToken, }); if (!res.success) { // TODO: modal @@ -38,6 +42,15 @@ function enter(e: KeyboardEvent) { if (e.key === "Enter") logIn(); } + async function captchaComplete(event: CustomEvent) { + captchaToken = event.detail.token; + } + + onMount(async () => { + setTimeout(() => { + shouldLoadCaptcha = true; + }, 500); + });
+
+ {#if shouldLoadCaptcha} + + {/if} +
diff --git a/src/routes/signup/+page.svelte b/src/routes/signup/+page.svelte index e1bd2a2..555b3f8 100644 --- a/src/routes/signup/+page.svelte +++ b/src/routes/signup/+page.svelte @@ -6,7 +6,7 @@ import { SuyuAPI } from "$lib/client/api"; import type { PageData } from "./$types"; import type { Writable } from "svelte/store"; - import { getContext } from "svelte"; + import { getContext, onMount } from "svelte"; const token = getContext>("token"); if ($token) goto("/account"); @@ -42,6 +42,14 @@ async function captchaComplete(event: CustomEvent) { captchaToken = event.detail.token; } + + let shouldLoadCaptcha = false; + + onMount(() => { + setTimeout(() => { + shouldLoadCaptcha = true; + }, 500); + });
- + {#if shouldLoadCaptcha} + + {/if}