Redirect users to landing on (app) routes, game page
This commit is contained in:
parent
d4180e5e4f
commit
05d0364f36
|
|
@ -8,11 +8,16 @@ export async function handle({ event, resolve }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const cookie = event.cookies.get(COOKIE_NAME);
|
const cookie = event.cookies.get(COOKIE_NAME);
|
||||||
if (!cookie) return await resolve(event);
|
if (!cookie) {
|
||||||
|
if (event.routeId.startsWith("(app)")) return new Response("", { status: 302, headers: { Location: "/landing" } });
|
||||||
|
return await resolve(event);
|
||||||
|
}
|
||||||
|
|
||||||
let user = await getUserFromSession(cookie, event.getClientAddress());
|
let user = await getUserFromSession(cookie, event.request.headers.get("x-forwarded-for") || event.getClientAddress());
|
||||||
if (!user) event.cookies.delete(COOKIE_NAME, { secure: !!process.env.PRODUCTION });
|
if (!user) {
|
||||||
else
|
event.cookies.delete(COOKIE_NAME, { secure: !!process.env.PRODUCTION });
|
||||||
|
if (event.routeId.startsWith("(app)")) return new Response("", { status: 302, headers: { Location: "/landing" } });
|
||||||
|
} else
|
||||||
event.locals.user = {
|
event.locals.user = {
|
||||||
_id: user._id,
|
_id: user._id,
|
||||||
username: user.username,
|
username: user.username,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
<!-- https://github.com/BillBuilt/sveltekit-hcaptcha -->
|
||||||
|
<script>
|
||||||
|
import { onMount, onDestroy } from "svelte";
|
||||||
|
import { browser } from "$app/environment";
|
||||||
|
import { HCAPTCHA_SITEKEY } from "$lib/constants";
|
||||||
|
|
||||||
|
let hcaptcha;
|
||||||
|
let hcaptchaWidgetID;
|
||||||
|
|
||||||
|
export let token = null;
|
||||||
|
// forces 1-way binding
|
||||||
|
let captchaToken;
|
||||||
|
$: token = captchaToken;
|
||||||
|
|
||||||
|
export let isValid = false;
|
||||||
|
// forces 1-way binding
|
||||||
|
let captchaValid;
|
||||||
|
$: isValid = captchaValid;
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
setTimeout(function () {
|
||||||
|
if (browser) {
|
||||||
|
hcaptcha = window.hcaptcha;
|
||||||
|
if (hcaptcha.render) {
|
||||||
|
hcaptchaWidgetID = hcaptcha.render("hcaptcha", {
|
||||||
|
sitekey: HCAPTCHA_SITEKEY,
|
||||||
|
size: "normal",
|
||||||
|
callback: onValidCaptcha,
|
||||||
|
"error-callback": onErrorCaptcha,
|
||||||
|
theme: "light"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
});
|
||||||
|
|
||||||
|
onDestroy(() => {
|
||||||
|
if (browser) {
|
||||||
|
hcaptcha = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function onValidCaptcha(e) {
|
||||||
|
//console.log('verified event', e)
|
||||||
|
captchaToken = e;
|
||||||
|
captchaValid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onErrorCaptcha(e) {
|
||||||
|
//console.log('error event', {error: e.error})
|
||||||
|
captchaToken = null;
|
||||||
|
captchaValid = false;
|
||||||
|
hcaptcha.reset(hcaptchaWidgetID);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<script src="https://js.hcaptcha.com/1/api.js?render=explicit" async defer></script>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<div id="hcaptcha" class="h-captcha" />
|
||||||
|
|
@ -2,6 +2,7 @@ export const COOKIE_NAME = ".ROWBLOX_SESSION_DO_NOT_SHARE";
|
||||||
export const USERNAME_REGEX = /[^A-Za-z0-9\-_ ]/g;
|
export const USERNAME_REGEX = /[^A-Za-z0-9\-_ ]/g;
|
||||||
export const MIN_USERNAME_LENGTH = 3;
|
export const MIN_USERNAME_LENGTH = 3;
|
||||||
export const MAX_USERNAME_LENGTH = 16;
|
export const MAX_USERNAME_LENGTH = 16;
|
||||||
export const MIN_PASSWORD_LENGTH = 0;
|
export const MIN_PASSWORD_LENGTH = 3;
|
||||||
export const INVITE_KEY_PREFIX = "rowblox-";
|
export const INVITE_KEY_PREFIX = "rowblox-";
|
||||||
export const SESSION_EXPIRE = 604800000;
|
export const SESSION_EXPIRE = 604800000;
|
||||||
|
export const HCAPTCHA_SITEKEY = "be5c40c1-13db-423c-878e-f3428e9fc841";
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ const assets = await db.collection("assets");
|
||||||
const invites = await db.collection("invites");
|
const invites = await db.collection("invites");
|
||||||
const avatars = await db.collection("avatars");
|
const avatars = await db.collection("avatars");
|
||||||
const sessions = await db.collection("sessions");
|
const sessions = await db.collection("sessions");
|
||||||
|
const friends = await db.collection("friends");
|
||||||
|
|
||||||
async function inc(collection) {
|
async function inc(collection) {
|
||||||
await collection.updateOne({ _id: "_inc" }, { $inc: { _inc: 1 } });
|
await collection.updateOne({ _id: "_inc" }, { $inc: { _inc: 1 } });
|
||||||
|
|
@ -50,7 +51,13 @@ export async function createUser(username, password, lastip) {
|
||||||
username,
|
username,
|
||||||
password: await hashPassword(password),
|
password: await hashPassword(password),
|
||||||
lastip,
|
lastip,
|
||||||
currency: 100
|
currency: 100,
|
||||||
|
joinDate: Date.now(),
|
||||||
|
lastOnline: Date.now(),
|
||||||
|
rank: 1,
|
||||||
|
activity: 1,
|
||||||
|
pendingFriendRequests: [],
|
||||||
|
bio: ""
|
||||||
};
|
};
|
||||||
|
|
||||||
const avatar = {
|
const avatar = {
|
||||||
|
|
@ -63,7 +70,8 @@ export async function createUser(username, password, lastip) {
|
||||||
LeftLegColor: 1,
|
LeftLegColor: 1,
|
||||||
RightLegColor: 1
|
RightLegColor: 1
|
||||||
},
|
},
|
||||||
Assets: {}
|
Wearing: [],
|
||||||
|
Inventory: []
|
||||||
};
|
};
|
||||||
|
|
||||||
await Promise.all([avatars.insertOne(avatar), await users.insertOne(user)]);
|
await Promise.all([avatars.insertOne(avatar), await users.insertOne(user)]);
|
||||||
|
|
@ -104,12 +112,12 @@ export async function deleteSession(session, lastip) {
|
||||||
const sessionDocument = await sessions.findOne({ _id: session });
|
const sessionDocument = await sessions.findOne({ _id: session });
|
||||||
if (!sessionDocument) return false;
|
if (!sessionDocument) return false;
|
||||||
|
|
||||||
const user = await users.findOne({ _id: sessionDocument._id });
|
const user = await users.findOne({ _id: sessionDocument.owner });
|
||||||
if (!user) return false;
|
if (!user) return false;
|
||||||
|
|
||||||
await users.updateOne({ _id: user._id }, { $set: { lastip } });
|
await users.updateOne({ _id: user._id }, { $set: { lastip } });
|
||||||
|
|
||||||
return await sessions.deleteOne({ session });
|
return await sessions.deleteOne({ _id: session });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getUser(query, projection) {
|
export async function getUser(query, projection) {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,15 @@
|
||||||
<script>
|
<script>
|
||||||
import Alert from "$lib/components/Alert.svelte";
|
import Alert from "$lib/components/Alert.svelte";
|
||||||
import Banner from "$lib/components/AdBanner.svelte";
|
import Banner from "$lib/components/AdBanner.svelte";
|
||||||
|
import { goto } from "$app/navigation";
|
||||||
|
|
||||||
/** @type {import('./$types').LayoutData} */
|
/** @type {import('./$types').LayoutData} */
|
||||||
export let data;
|
export let data;
|
||||||
|
|
||||||
|
async function logout() {
|
||||||
|
const response = await fetch("/logout", { method: "GET" });
|
||||||
|
await goto("/landing");
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="navbar scrolling-background text-lg py-1 text-white">
|
<div class="navbar scrolling-background text-lg py-1 text-white">
|
||||||
|
|
@ -31,7 +37,7 @@
|
||||||
<a class="block px-4 py-0.5 text-gray-700" href="/my/profile">Profile</a>
|
<a class="block px-4 py-0.5 text-gray-700" href="/my/profile">Profile</a>
|
||||||
<a class="block px-4 py-0.5 text-gray-700" href="/my/settings">Settings</a>
|
<a class="block px-4 py-0.5 text-gray-700" href="/my/settings">Settings</a>
|
||||||
</div>
|
</div>
|
||||||
<a class="block px-4 py-0.5 text-red-700" href="/logout">Logout</a>
|
<span class="block px-4 py-0.5 text-red-700 hover:cursor-pointer" on:click={logout}>Logout</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,38 +1,105 @@
|
||||||
|
<script>
|
||||||
|
import UserListed from "$lib/components/UserListed.svelte";
|
||||||
|
export let game;
|
||||||
|
let state = 0;
|
||||||
|
|
||||||
|
function updateState(newState) {
|
||||||
|
state = newState;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>Games - Rowblox</title>
|
<title>{game?.name} - Rowblox</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
|
<div class="mx-auto rounded-lg shadow border border-gray-300 p-4 divide-y">
|
||||||
|
<div class="flex">
|
||||||
|
<img class="float-left h-[22.5rem]" alt="" src="/img/background.png" />
|
||||||
|
<div class="w-full flex flex-col pl-4">
|
||||||
|
<div class="max-w-[22.9rem]">
|
||||||
|
<p class="text-3xl font-bold truncate">{game?.name}</p>
|
||||||
|
<p class="text-zinc-500">By <a class="font-bold text-blue-500" href="/users/{game?.creator?._id}">{game?.creator?.username}</a></p>
|
||||||
|
</div>
|
||||||
|
<span class="flex-grow" />
|
||||||
|
<div class="flex">
|
||||||
|
<a class="flex-auto text-lg text-blue-500 px-4 py-0.5 rounded shadow-lg border-2 border-blue-500 hover:bg-opacity-50 hover:bg-blue-500 mr-0.5 mb-1 text-center" href="/games/{game?.id}/edit">
|
||||||
|
Edit
|
||||||
|
</a>
|
||||||
|
<button class="flex-auto text-lg text-blue-500 px-4 py-0.5 rounded shadow-lg border-2 border-blue-500 hover:bg-opacity-50 hover:bg-blue-500 ml-0.5 mb-1">Host</button>
|
||||||
|
</div>
|
||||||
|
<button class="text-2xl text-blue-500 px-4 py-2 rounded shadow-lg border-2 border-blue-500 hover:bg-opacity-50 hover:bg-blue-500">Play</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="accent py-0.5 flex-none shadow-md"></div>
|
<div class="rounded-lg shadow border border-gray-300 p-2 mt-4">
|
||||||
|
<div class="flex flex-row text-xl text-center text-black">
|
||||||
|
<button
|
||||||
|
class="flex-auto {state == 0 && 'font-bold text-black text-opacity-30'}"
|
||||||
|
on:click={() => {
|
||||||
|
updateState(0);
|
||||||
|
}}>About</button
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="flex-auto {state == 1 && 'font-bold text-black text-opacity-30'}"
|
||||||
|
on:click={() => {
|
||||||
|
updateState(1);
|
||||||
|
}}>Store</button
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="flex-auto {state == 2 && 'font-bold text-black text-opacity-30'}"
|
||||||
|
on:click={() => {
|
||||||
|
updateState(2);
|
||||||
|
}}>Players</button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="my-4 flex-grow">
|
{#if state == 0}
|
||||||
|
<div class="rounded-lg shadow border border-gray-300 p-2 mt-4 divide-y">
|
||||||
<div class="container"><div class="carbon-gradient rounded shadow-lg p-4"><div class="flex mb-4"><img class="w-[38rem] float-left" alt="">
|
<div class="pb-2">
|
||||||
<div class="pl-4 w-full flex flex-col place-content-between"><div class="max-w-[22.9rem]"><p class="text-3xl font-bold text-ellipsis overflow-clip">Test Game</p>
|
<p class="px-2 py-1 font-bold text-xl">Description</p>
|
||||||
<p class="text-zinc-500">By <a class="font-bold text-blue-500 hover:text-zinc-300" href="/users/1">Rowblox</a></p></div>
|
<p class="px-2 text-black text-opacity-80">Test Game</p>
|
||||||
<div class="flex flex-col"><div class="w-full flex flex-col mb-4 mx-auto"><div class="flex flex-row place-content-between mb-0.5 px-1"><div class="flex flex-row text-green-200"><i class="fa fa-thumbs-up text-4xl hover:text-green-300" aria-hidden="true"></i>
|
</div>
|
||||||
<p class="my-auto text-lg px-2 select-none font-bold">0</p></div>
|
<div class="flex flex-row pb-2">
|
||||||
<div class="flex flex-row text-red-200"><p class="my-auto text-lg px-2 select-none font-bold">0</p>
|
<div class="mx-4 mt-4 text-center flex-grow">
|
||||||
<i class="fa fa-thumbs-down text-4xl hover:text-red-300" aria-hidden="true"></i></div></div>
|
<p class="text-zinc-500 inline-block">Players</p>
|
||||||
<div class="relative"><div class="py-0.5 bg-red-400 rounded-lg absolute left-0 w-full "></div>
|
<p class="">0</p>
|
||||||
<div class="py-0.5 bg-green-400 rounded-lg absolute left-0" style="width: 50%;"></div></div></div>
|
</div>
|
||||||
<a class=" text-blue-500 accent shadow-lg rounded hover:text-zinc-300 mb-2 p-1 text-center flex-grow ml-1 border-[3px] border-blue-500" href="/games/1/edit">Edit Game</a></div>
|
<div class="mx-4 mt-4 text-center flex-grow">
|
||||||
<button class="text-xl bg-white hover:text-zinc-300 py-3 w-full rounded shadow-lg accent text-blue-500 border-blue-500 border-[3px]">Play</button></div></div></div>
|
<p class="text-zinc-500 inline-block">Visits</p>
|
||||||
<div class="divider-0"></div>
|
<p class="">0</p>
|
||||||
<p class="py-2">Test Game</p>
|
</div>
|
||||||
<div class="divider-0"></div>
|
<div class="mx-4 mt-4 text-center flex-grow">
|
||||||
<div class="flex flex-row"><div class="m-4 text-center flex-grow"><p class="text-zinc-500 inline-block">Players</p>
|
<p class="text-zinc-500 inline-block">Created</p>
|
||||||
<p class="">0</p></div>
|
<p class="">10/10/22</p>
|
||||||
<div class="m-4 text-center flex-grow"><p class="text-zinc-500 inline-block">Visits</p>
|
</div>
|
||||||
<p class="">0</p></div>
|
<div class="mx-4 mt-4 text-center flex-grow">
|
||||||
<div class="m-4 text-center flex-grow"><p class="text-zinc-500 inline-block">Created</p>
|
<p class="text-zinc-500 inline-block">Updated</p>
|
||||||
<p class="">10/10/22</p></div>
|
<p class="">10/10/22</p>
|
||||||
<div class="m-4 text-center flex-grow"><p class="text-zinc-500 inline-block">Updated</p>
|
</div>
|
||||||
<p class="">10/10/22</p></div>
|
<div class="mx-4 mt-4 text-center flex-grow">
|
||||||
<div class="m-4 text-center flex-grow"><p class="text-zinc-500 inline-block">Max Players</p>
|
<p class="text-zinc-500 inline-block">Max Players</p>
|
||||||
<p class="">All</p></div></div>
|
<p class="">No Limit</p>
|
||||||
<div class="divider-0"></div></div>
|
</div>
|
||||||
<div class="carbon-gradient rounded shadow-lg p-2 mt-4"><div class="flex flex-row text-xl text-center"><a href="#players" class="flex-auto text-zinc-600">Players</a>
|
</div>
|
||||||
<a href="#gamepasses" class="flex-auto hover:cursor-pointer hover:text-zinc-300">Gamepasses</a></div></div>
|
</div>
|
||||||
<div class="carbon-gradient rounded shadow-lg p-[0.7rem] mt-2"><p class="p-1.5 text-base">No players connected</p></div></div>
|
{:else if state == 1}
|
||||||
|
<div class="rounded-lg shadow border border-gray-300 p-2 mt-4">
|
||||||
|
<p class="px-2 py-1 font-bold text-xl">Gamepasses</p>
|
||||||
|
<p class="px-2 text-black text-opacity-80">This store does not have any gamepasses for sale.</p>
|
||||||
|
</div>
|
||||||
|
{:else if state == 2}
|
||||||
|
<div class="rounded-lg shadow border border-gray-300 p-2 mt-4">
|
||||||
|
<p class="px-2 py-1 font-bold text-xl">Players</p>
|
||||||
|
<p class="px-2 text-black text-opacity-80">This game does not have any players.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="rounded-lg shadow border border-gray-300 p-2 mt-4">
|
||||||
|
<p class="px-2 py-1 font-bold text-xl mb-1">Players</p>
|
||||||
|
<UserListed />
|
||||||
|
<UserListed />
|
||||||
|
<UserListed />
|
||||||
|
<UserListed />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
|
||||||
|
|
@ -14,18 +14,3 @@
|
||||||
<button class="px-3 py-1 rounded border-2 border-blue-500 text-blue-500 shadow-lg hover:bg-blue-200">Add Friend</button>
|
<button class="px-3 py-1 rounded border-2 border-blue-500 text-blue-500 shadow-lg hover:bg-blue-200">Add Friend</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="shadow-lg w-96 h-96 p-2 rounded-lg border-2 border-gray-300 mt-2 mx-1 inline-block">
|
|
||||||
<div class="font-bold text-bold mt-72 mx-1">
|
|
||||||
<p class="font-bold text-[18px] mx-20">This is my description!</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex place-content-between m">
|
|
||||||
<div class="w-full rounded-lg flex shadow border border-gray-300 p-2 mt-2 mr-1">
|
|
||||||
<p class="font-bold text-2xl">Friends</p>
|
|
||||||
</div>
|
|
||||||
<div class="w-full rounded-lg flex shadow border border-gray-300 p-2 mt-2 ml-1">
|
|
||||||
<p class="font-bold text-2xl">Games</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ export const actions = {
|
||||||
const correctPassword = await compareHash(password, user.password);
|
const correctPassword = await compareHash(password, user.password);
|
||||||
if (!correctPassword) return invalid(400, { error: "password" });
|
if (!correctPassword) return invalid(400, { error: "password" });
|
||||||
|
|
||||||
cookies.set(COOKIE_NAME, await createSession(user._id, getClientAddress()), { secure: !!process.env.PRODUCTION });
|
cookies.set(COOKIE_NAME, await createSession(user._id, request.headers.get("x-forwarded-for") || getClientAddress()), { secure: !!process.env.PRODUCTION });
|
||||||
throw redirect(302, "/");
|
throw redirect(302, "/");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
<img class="mx-auto mb-2" src="/favicon.png" alt="" />
|
<img class="mx-auto mb-2" src="/favicon.png" alt="" />
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<input
|
<input
|
||||||
class="rounded-lg w-72 px-4 border-2 {form?.error == 'username' ? 'border-red-500' : 'border-blue-500'} text-black mx-auto my-0.5 py-2"
|
class="rounded-lg w-[19rem] px-4 border-2 {form?.error == 'username' ? 'border-red-500' : 'border-blue-500'} text-black mx-auto my-0.5 py-2"
|
||||||
type="username"
|
type="username"
|
||||||
name="username"
|
name="username"
|
||||||
id="username"
|
id="username"
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
value={form?.username ?? ""}
|
value={form?.username ?? ""}
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
class="rounded-lg w-72 px-4 border-2 {form?.error == 'password' ? 'border-red-500' : 'border-blue-500'} text-black mx-auto my-0.5 py-2"
|
class="rounded-lg w-[19rem] px-4 border-2 {form?.error == 'password' ? 'border-red-500' : 'border-blue-500'} text-black mx-auto my-0.5 py-2"
|
||||||
type="password"
|
type="password"
|
||||||
name="password"
|
name="password"
|
||||||
id="password"
|
id="password"
|
||||||
|
|
@ -28,8 +28,8 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{#if form?.message}
|
{#if form?.message}
|
||||||
<p class="rounded w-72 px-4 border-[3px] border-red-500 text-red-500 mx-auto my-1.5 py-1.5 hover:bg-opacity-50 hover:bg-red-500">{form?.message}</p>
|
<p class="rounded w-[19rem] px-4 border-[3px] border-red-500 text-red-500 mx-auto my-1.5 py-1.5 hover:bg-opacity-50 hover:bg-red-500">{form?.message}</p>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="h-captcha" data-sitekey="30000000-ffff-ffff-ffff-000000000003" />
|
<div class="h-captcha" data-sitekey="30000000-ffff-ffff-ffff-000000000003" />
|
||||||
<button class="my-1.5 text-xl text-white px-4 py-1.5 w-full rounded shadow-lg border-2 border-blue-500 hover:bg-opacity-50 hover:bg-blue-500">Login</button>
|
<button class="my-1.5 text-xl text-white px-4 py-1.5 w-[19rem] rounded shadow-lg border-2 border-blue-500 hover:bg-opacity-50 hover:bg-blue-500">Login</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import { deleteSession } from "$lib/database";
|
||||||
import { COOKIE_NAME } from "$lib/constants";
|
import { COOKIE_NAME } from "$lib/constants";
|
||||||
|
|
||||||
/** @type {import('./$types').RequestHandler} */
|
/** @type {import('./$types').RequestHandler} */
|
||||||
export async function GET({ cookies, getClientAddress }) {
|
export async function GET({ request, cookies, getClientAddress }) {
|
||||||
const session = cookies.get(COOKIE_NAME);
|
const session = cookies.get(COOKIE_NAME);
|
||||||
if (!session)
|
if (!session)
|
||||||
return new Response("", {
|
return new Response("", {
|
||||||
|
|
@ -11,7 +11,7 @@ export async function GET({ cookies, getClientAddress }) {
|
||||||
});
|
});
|
||||||
|
|
||||||
cookies.delete(COOKIE_NAME, { secure: !!process.env.PRODUCTION });
|
cookies.delete(COOKIE_NAME, { secure: !!process.env.PRODUCTION });
|
||||||
await deleteSession(session, getClientAddress());
|
await deleteSession(session, request.headers.get("x-forwarded-for") || getClientAddress());
|
||||||
|
|
||||||
return new Response("", {
|
return new Response("", {
|
||||||
status: 302,
|
status: 302,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { invalid, redirect } from "@sveltejs/kit";
|
import { invalid, redirect } from "@sveltejs/kit";
|
||||||
import { createUser, createSession } from "$lib/database";
|
import { createUser, createSession, getUser } from "$lib/database";
|
||||||
import { MIN_USERNAME_LENGTH, MAX_USERNAME_LENGTH, USERNAME_REGEX, MIN_PASSWORD_LENGTH, INVITE_KEY_PREFIX, COOKIE_NAME } from "$lib/constants";
|
import { MIN_USERNAME_LENGTH, MAX_USERNAME_LENGTH, USERNAME_REGEX, MIN_PASSWORD_LENGTH, INVITE_KEY_PREFIX, COOKIE_NAME, HCAPTCHA_SITEKEY } from "$lib/constants";
|
||||||
|
|
||||||
/** @type {import('./$types').Actions} */
|
/** @type {import('./$types').Actions} */
|
||||||
export const actions = {
|
export const actions = {
|
||||||
|
|
@ -13,6 +13,7 @@ export const actions = {
|
||||||
const password = data.get("password");
|
const password = data.get("password");
|
||||||
const confirm_password = data.get("confirm_password");
|
const confirm_password = data.get("confirm_password");
|
||||||
const invite_key = data.get("invite_key");
|
const invite_key = data.get("invite_key");
|
||||||
|
const hcaptcha_response = data.get("h-captcha-response");
|
||||||
|
|
||||||
if (username.length < MIN_USERNAME_LENGTH || username.length > MAX_USERNAME_LENGTH || new RegExp(USERNAME_REGEX).test(username))
|
if (username.length < MIN_USERNAME_LENGTH || username.length > MAX_USERNAME_LENGTH || new RegExp(USERNAME_REGEX).test(username))
|
||||||
return invalid(400, {
|
return invalid(400, {
|
||||||
|
|
@ -35,8 +36,37 @@ export const actions = {
|
||||||
error: "invite_key"
|
error: "invite_key"
|
||||||
});
|
});
|
||||||
|
|
||||||
const user = await createUser(username, password, getClientAddress());
|
if (!hcaptcha_response)
|
||||||
cookies.set(COOKIE_NAME, await createSession(user, getClientAddress()), { secure: !!process.env.PRODUCTION });
|
return invalid(400, {
|
||||||
|
username,
|
||||||
|
invite_key,
|
||||||
|
error: "hcaptcha"
|
||||||
|
});
|
||||||
|
|
||||||
|
const existingUser = await getUser({ username }, { _id: true });
|
||||||
|
if (existingUser)
|
||||||
|
return invalid(400, {
|
||||||
|
username,
|
||||||
|
invite_key,
|
||||||
|
error: "username"
|
||||||
|
});
|
||||||
|
|
||||||
|
const hcaptcha = await fetch("https://hcaptcha.com/siteverify", {
|
||||||
|
method: "POST",
|
||||||
|
body: `response=${hcaptcha_response}&secret=${process.env.HCAPTCHA_SECRET}`,
|
||||||
|
headers: { "content-type": "application/x-www-form-urlencoded" }
|
||||||
|
});
|
||||||
|
|
||||||
|
const hcaptchaBody = await hcaptcha.json();
|
||||||
|
if (!hcaptchaBody.success)
|
||||||
|
return invalid(400, {
|
||||||
|
username,
|
||||||
|
invite_key,
|
||||||
|
error: "hcaptcha"
|
||||||
|
});
|
||||||
|
|
||||||
|
const user = await createUser(username, password, request.headers.get("x-forwarded-for") || getClientAddress());
|
||||||
|
cookies.set(COOKIE_NAME, await createSession(user, request.headers.get("x-forwarded-for") || getClientAddress()), { secure: !!process.env.PRODUCTION });
|
||||||
throw redirect(302, "/");
|
throw redirect(302, "/");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,20 @@
|
||||||
<script>
|
<script>
|
||||||
|
import HCaptcha from "../../../lib/components/hCaptcha.svelte";
|
||||||
/** @type {import('./$types').ActionData} */ export let form;
|
/** @type {import('./$types').ActionData} */ export let form;
|
||||||
|
|
||||||
|
let captchaValid = false;
|
||||||
|
let captchaToken;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>Register - Rowblox</title>
|
<title>Register - Rowblox</title>
|
||||||
<script src="https://js.hcaptcha.com/1/api.js" async defer></script>
|
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<form method="POST" class="max-w-lg mx-auto text-center text-white">
|
<form method="POST" class="max-w-lg mx-auto text-center text-white">
|
||||||
<img class="mx-auto mb-2" src="/favicon.png" alt="" />
|
<img class="mx-auto mb-2" src="/favicon.png" alt="" />
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<input
|
<input
|
||||||
class="rounded-lg w-72 px-4 border-2 {form?.error == 'username' ? 'border-red-500' : 'border-blue-500'} text-black mx-auto my-0.5 py-2"
|
class="rounded-lg w-[19rem] px-4 border-2 {form?.error == 'username' ? 'border-red-500' : 'border-blue-500'} text-black mx-auto my-0.5 py-2"
|
||||||
required
|
required
|
||||||
type="username"
|
type="username"
|
||||||
name="username"
|
name="username"
|
||||||
|
|
@ -20,7 +23,7 @@
|
||||||
value={form?.username ?? ""}
|
value={form?.username ?? ""}
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
class="rounded-lg w-72 px-4 border-2 {form?.error == 'password' ? 'border-red-500' : 'border-blue-500'} text-black mx-auto my-0.5 py-2"
|
class="rounded-lg w-[19rem] px-4 border-2 {form?.error == 'password' ? 'border-red-500' : 'border-blue-500'} text-black mx-auto my-0.5 py-2"
|
||||||
required
|
required
|
||||||
type="password"
|
type="password"
|
||||||
name="password"
|
name="password"
|
||||||
|
|
@ -28,7 +31,7 @@
|
||||||
placeholder="Password"
|
placeholder="Password"
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
class="rounded-lg w-72 px-4 border-2 {form?.error == 'password' ? 'border-red-500' : 'border-blue-500'} text-black mx-auto my-0.5 py-2"
|
class="rounded-lg w-[19rem] px-4 border-2 {form?.error == 'password' ? 'border-red-500' : 'border-blue-500'} text-black mx-auto my-0.5 py-2"
|
||||||
required
|
required
|
||||||
type="password"
|
type="password"
|
||||||
name="confirm_password"
|
name="confirm_password"
|
||||||
|
|
@ -36,7 +39,7 @@
|
||||||
placeholder="Confirm Password"
|
placeholder="Confirm Password"
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
class="rounded-lg w-72 px-4 border-2 {form?.error == 'invite_key' ? 'border-red-500' : 'border-blue-500'} text-black mx-auto my-0.5 py-2"
|
class="rounded-lg w-[19rem] px-4 border-2 {form?.error == 'invite_key' ? 'border-red-500' : 'border-blue-500'} text-black mx-auto mt-0.5 mb-1.5 py-2"
|
||||||
required
|
required
|
||||||
type="text"
|
type="text"
|
||||||
name="invite_key"
|
name="invite_key"
|
||||||
|
|
@ -46,8 +49,10 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{#if form?.message}
|
{#if form?.message}
|
||||||
<p class="rounded w-72 px-4 border-2 border-red-500 text-red-500 bg-red-500 mx-auto my-1.5 py-1.5 bg-opacity-50">{form?.message}</p>
|
<p class="rounded w-[19rem] px-4 border-2 border-red-500 text-red-500 bg-red-500 mx-auto my-1.5 py-1.5 bg-opacity-50">{form?.message}</p>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="h-captcha" data-sitekey="30000000-ffff-ffff-ffff-000000000003" />
|
<div class={form?.error == "hcaptcha" && "border-2 border-red-500"}>
|
||||||
<button class="my-1.5 text-xl text-white px-4 py-1.5 w-full rounded shadow-lg border-2 border-blue-500 hover:bg-opacity-50 hover:bg-blue-500">Register</button>
|
<HCaptcha bind:token={captchaToken} bind:isValid={captchaValid} />
|
||||||
|
</div>
|
||||||
|
<button class="my-1.5 text-xl text-white px-4 py-1.5 w-[19rem] rounded shadow-lg border-2 border-blue-500 hover:bg-opacity-50 hover:bg-blue-500">Register</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue