Accounts 🎉
This commit is contained in:
parent
1d03a5afc9
commit
d4180e5e4f
|
|
@ -1,8 +1,27 @@
|
||||||
|
import { COOKIE_NAME } from "$lib/constants";
|
||||||
|
import { getUserFromSession } from "$lib/database";
|
||||||
|
|
||||||
/** @type {import('@sveltejs/kit').Handle} */
|
/** @type {import('@sveltejs/kit').Handle} */
|
||||||
export async function handle({ event, resolve }) {
|
export async function handle({ event, resolve }) {
|
||||||
if (event.url.pathname !== "/maintenance" && process.env.MAINTENANCE) {
|
if (event.url.pathname !== "/maintenance" && process.env.MAINTENANCE) {
|
||||||
return new Response("", { status: 302, headers: { Location: "/maintenance" } });
|
return new Response("", { status: 302, headers: { Location: "/maintenance" } });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cookie = event.cookies.get(COOKIE_NAME);
|
||||||
|
if (!cookie) return await resolve(event);
|
||||||
|
|
||||||
|
let user = await getUserFromSession(cookie, event.getClientAddress());
|
||||||
|
if (!user) event.cookies.delete(COOKIE_NAME, { secure: !!process.env.PRODUCTION });
|
||||||
|
else
|
||||||
|
event.locals.user = {
|
||||||
|
_id: user._id,
|
||||||
|
username: user.username,
|
||||||
|
currency: user.currency,
|
||||||
|
thumbnails: {
|
||||||
|
headshot: "https://cdn.discordapp.com/attachments/1025862249962819684/1028788210303766558/2022.10-887.png",
|
||||||
|
bodyshot: "https://media.tenor.com/Lo0GvkoTFR4AAAAd/xbox-xbox-avatar.gif"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return await resolve(event);
|
return await resolve(event);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<div class="max-w-[6rem] inline-block">
|
<div class="max-w-[6rem] inline-block">
|
||||||
<a href="/users/1">
|
<a href="/users/1">
|
||||||
<img class="rounded-full border-2 border-grey-300 w-24 h-24 shadow-lg" src="" alt="" />
|
<img class="rounded-full border-2 border-grey-300 w-24 h-24 shadow-lg" src="https://cdn.discordapp.com/attachments/1026648537741672490/1029210746501996616/2022.10-550.png" alt="" />
|
||||||
<p class="text-center">Rowblox</p>
|
<p class="text-center">bucks</p>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -28,17 +28,17 @@ async function hashPassword(plaintext) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function compareHash(plaintext, hash) {
|
export async function compareHash(plaintext, hash) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
compare(plaintext, hash, (err, result) => resolve(result));
|
compare(plaintext, hash, (err, result) => resolve(result));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function genSession() {
|
function genSession() {
|
||||||
return randomBytes(64).toString("base64");
|
return randomBytes(64).toString("base64");
|
||||||
}
|
}
|
||||||
|
|
||||||
async function genInvite() {
|
function genInvite() {
|
||||||
return `${INVITE_KEY_PREFIX}${randomUUID()}`;
|
return `${INVITE_KEY_PREFIX}${randomUUID()}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -49,7 +49,8 @@ export async function createUser(username, password, lastip) {
|
||||||
_id,
|
_id,
|
||||||
username,
|
username,
|
||||||
password: await hashPassword(password),
|
password: await hashPassword(password),
|
||||||
lastip
|
lastip,
|
||||||
|
currency: 100
|
||||||
};
|
};
|
||||||
|
|
||||||
const avatar = {
|
const avatar = {
|
||||||
|
|
@ -65,17 +66,55 @@ export async function createUser(username, password, lastip) {
|
||||||
Assets: {}
|
Assets: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
await avatars.insertOne(avatar);
|
await Promise.all([avatars.insertOne(avatar), await users.insertOne(user)]);
|
||||||
return await users.insertOne(user);
|
|
||||||
|
return _id;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createSession(_id, lastip) {
|
export async function createSession(_id, lastip) {
|
||||||
const user = await users.findOne({ _id });
|
const user = await users.findOne({ _id });
|
||||||
if (!user) throw new Error("That user doesn't exist");
|
if (!user) return false;
|
||||||
|
|
||||||
return await sessions.insertOne({
|
const session = genSession();
|
||||||
_id,
|
|
||||||
session: genSession(),
|
await sessions.insertOne({
|
||||||
|
_id: session,
|
||||||
|
owner: _id,
|
||||||
expires: Date.now() + SESSION_EXPIRE
|
expires: Date.now() + SESSION_EXPIRE
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await users.updateOne({ _id: user._id }, { $set: { lastip } });
|
||||||
|
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getUserFromSession(session, lastip) {
|
||||||
|
const sessionDocument = await sessions.findOne({ _id: session });
|
||||||
|
if (!sessionDocument) return false;
|
||||||
|
|
||||||
|
const user = await users.findOne({ _id: sessionDocument.owner });
|
||||||
|
if (!user) return false;
|
||||||
|
|
||||||
|
await users.updateOne({ _id: user._id }, { $set: { lastip } });
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteSession(session, lastip) {
|
||||||
|
const sessionDocument = await sessions.findOne({ _id: session });
|
||||||
|
if (!sessionDocument) return false;
|
||||||
|
|
||||||
|
const user = await users.findOne({ _id: sessionDocument._id });
|
||||||
|
if (!user) return false;
|
||||||
|
|
||||||
|
await users.updateOne({ _id: user._id }, { $set: { lastip } });
|
||||||
|
|
||||||
|
return await sessions.deleteOne({ session });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getUser(query, projection) {
|
||||||
|
const user = await users.findOne(query, { projection });
|
||||||
|
if (!user) return false;
|
||||||
|
|
||||||
|
return user;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
/** @type {import('./$types').LayoutServerLoad} */
|
||||||
|
export function load({ locals }) {
|
||||||
|
return { user: locals.user };
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
<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";
|
||||||
|
|
||||||
|
/** @type {import('./$types').LayoutData} */
|
||||||
|
export let data;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="navbar scrolling-background text-lg py-1 text-white">
|
<div class="navbar scrolling-background text-lg py-1 text-white">
|
||||||
|
|
@ -16,10 +19,9 @@
|
||||||
<img class="h-6 mr-1.5" src="/img/rowbux.png" alt="" />
|
<img class="h-6 mr-1.5" src="/img/rowbux.png" alt="" />
|
||||||
100
|
100
|
||||||
</a>
|
</a>
|
||||||
<p class="mx-2 text-2xl">|</p>
|
<div class="border-l-2 border-blue-500 pl-1.5 ml-1 relative inline-block dropdown">
|
||||||
<div class="ml-1 relative inline-block dropdown">
|
|
||||||
<button class="hover:text-zinc-200 truncate" href="/my/profile"
|
<button class="hover:text-zinc-200 truncate" href="/my/profile"
|
||||||
>Only16Characters
|
>{data.user?.username}
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-4 h-4 inline-block">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-4 h-4 inline-block">
|
||||||
<path fill-rule="evenodd" d="M12.53 16.28a.75.75 0 01-1.06 0l-7.5-7.5a.75.75 0 011.06-1.06L12 14.69l6.97-6.97a.75.75 0 111.06 1.06l-7.5 7.5z" clip-rule="evenodd" />
|
<path fill-rule="evenodd" d="M12.53 16.28a.75.75 0 01-1.06 0l-7.5-7.5a.75.75 0 011.06-1.06L12 14.69l6.97-6.97a.75.75 0 111.06 1.06l-7.5 7.5z" clip-rule="evenodd" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import Game from "$lib/components/GameListed.svelte";
|
import Game from "$lib/components/GameListed.svelte";
|
||||||
import Friend from "$lib/components/UserListed.svelte";
|
import Friend from "$lib/components/UserListed.svelte";
|
||||||
|
import { page } from "$app/stores";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
|
|
@ -8,8 +9,8 @@
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<img class="rounded-full border-2 border-grey-300 w-36 h-36 mx-2 shadow-lg" src="" alt="" />
|
<img class="rounded-full border-2 border-grey-300 w-36 h-36 mx-2 shadow-lg" src={$page.data.user?.thumbnails.headshot} alt="" />
|
||||||
<p class="mx-2 font-bold text-3xl">Hello, Only16Characters!</p>
|
<p class="mx-2 font-bold text-3xl">Hello, {$page.data.user?.username}!</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="my-2">
|
<div class="my-2">
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { invalid, redirect } from "@sveltejs/kit";
|
||||||
|
import { getUser, compareHash, createSession } from "$lib/database";
|
||||||
|
import { MIN_USERNAME_LENGTH, MAX_USERNAME_LENGTH, USERNAME_REGEX, MIN_PASSWORD_LENGTH, INVITE_KEY_PREFIX, COOKIE_NAME } from "$lib/constants";
|
||||||
|
|
||||||
|
/** @type {import('./$types').Actions} */
|
||||||
|
export const actions = {
|
||||||
|
default: async ({ cookies, request, getClientAddress }) => {
|
||||||
|
const session = cookies.get(COOKIE_NAME);
|
||||||
|
if (session) throw redirect(302, "/");
|
||||||
|
|
||||||
|
const data = await request.formData();
|
||||||
|
const username = data.get("username");
|
||||||
|
const password = data.get("password");
|
||||||
|
|
||||||
|
const user = await getUser({ username }, { password: true });
|
||||||
|
if (!user) return invalid(400, { error: "username" });
|
||||||
|
|
||||||
|
const correctPassword = await compareHash(password, user.password);
|
||||||
|
if (!correctPassword) return invalid(400, { error: "password" });
|
||||||
|
|
||||||
|
cookies.set(COOKIE_NAME, await createSession(user._id, getClientAddress()), { secure: !!process.env.PRODUCTION });
|
||||||
|
throw redirect(302, "/");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -1,18 +1,35 @@
|
||||||
<script>
|
<script>
|
||||||
let username, passwd;
|
/** @type {import('./$types').ActionData} */ export let form;
|
||||||
|
|
||||||
function submit() {}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>Login - Rowblox</title>
|
<title>Login - Rowblox</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<form on:submit|preventDefault={submit} 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" 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 class="rounded-lg w-72 px-4 border-2 border-blue-500 text-black mx-auto my-1.5 py-2" type="username" name="username" id="username" placeholder="Username" bind:value={username} />
|
<input
|
||||||
<input class="rounded-lg w-72 px-4 border-2 border-blue-500 text-black mx-auto my-1.5 py-2" type="password" name="passwd" id="passwd" placeholder="Password" bind:value={passwd} />
|
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"
|
||||||
|
type="username"
|
||||||
|
name="username"
|
||||||
|
id="username"
|
||||||
|
placeholder="Username"
|
||||||
|
required
|
||||||
|
value={form?.username ?? ""}
|
||||||
|
/>
|
||||||
|
<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"
|
||||||
|
type="password"
|
||||||
|
name="password"
|
||||||
|
id="password"
|
||||||
|
placeholder="Password"
|
||||||
|
required
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<input type="submit" class="my-1.5 text-xl text-white px-4 py-1 w-full rounded shadow-lg border-2 border-blue-500 hover:cursor-pointer" value="Login" />
|
{#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>
|
||||||
|
{/if}
|
||||||
|
<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>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { deleteSession } from "$lib/database";
|
||||||
|
import { COOKIE_NAME } from "$lib/constants";
|
||||||
|
|
||||||
|
/** @type {import('./$types').RequestHandler} */
|
||||||
|
export async function GET({ cookies, getClientAddress }) {
|
||||||
|
const session = cookies.get(COOKIE_NAME);
|
||||||
|
if (!session)
|
||||||
|
return new Response("", {
|
||||||
|
status: 302,
|
||||||
|
headers: { location: "/landing" }
|
||||||
|
});
|
||||||
|
|
||||||
|
cookies.delete(COOKIE_NAME, { secure: !!process.env.PRODUCTION });
|
||||||
|
await deleteSession(session, getClientAddress());
|
||||||
|
|
||||||
|
return new Response("", {
|
||||||
|
status: 302,
|
||||||
|
headers: { location: "/landing" }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -6,7 +6,7 @@ import { MIN_USERNAME_LENGTH, MAX_USERNAME_LENGTH, USERNAME_REGEX, MIN_PASSWORD_
|
||||||
export const actions = {
|
export const actions = {
|
||||||
default: async ({ cookies, request, getClientAddress }) => {
|
default: async ({ cookies, request, getClientAddress }) => {
|
||||||
const session = cookies.get(COOKIE_NAME);
|
const session = cookies.get(COOKIE_NAME);
|
||||||
if (session) return redirect(302, "/");
|
if (session) throw redirect(302, "/landing");
|
||||||
|
|
||||||
const data = await request.formData();
|
const data = await request.formData();
|
||||||
const username = data.get("username");
|
const username = data.get("username");
|
||||||
|
|
@ -36,9 +36,7 @@ export const actions = {
|
||||||
});
|
});
|
||||||
|
|
||||||
const user = await createUser(username, password, getClientAddress());
|
const user = await createUser(username, password, getClientAddress());
|
||||||
console.log(user);
|
cookies.set(COOKIE_NAME, await createSession(user, getClientAddress()), { secure: !!process.env.PRODUCTION });
|
||||||
|
throw redirect(302, "/");
|
||||||
cookies.set(COOKIE_NAME, "murder row");
|
|
||||||
return invalid(500, { message: "Dis shit does NOT WORK!" });
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,10 @@
|
||||||
</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" 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 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-72 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 +20,7 @@
|
||||||
value={form?.username ?? ""}
|
value={form?.username ?? ""}
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
class="rounded 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-72 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 +28,7 @@
|
||||||
placeholder="Password"
|
placeholder="Password"
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
class="rounded 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-72 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 +36,7 @@
|
||||||
placeholder="Confirm Password"
|
placeholder="Confirm Password"
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
class="rounded 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-72 px-4 border-2 {form?.error == 'invite_key' ? 'border-red-500' : 'border-blue-500'} text-black mx-auto my-0.5 py-2"
|
||||||
required
|
required
|
||||||
type="text"
|
type="text"
|
||||||
name="invite_key"
|
name="invite_key"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue