Accounts 🎉

This commit is contained in:
I-Have-An-Issue 2022-10-10 22:40:43 -04:00
parent 1d03a5afc9
commit d4180e5e4f
No known key found for this signature in database
GPG Key ID: E55435DEA0825091
11 changed files with 159 additions and 35 deletions

View File

@ -1,8 +1,27 @@
import { COOKIE_NAME } from "$lib/constants";
import { getUserFromSession } from "$lib/database";
/** @type {import('@sveltejs/kit').Handle} */
export async function handle({ event, resolve }) {
if (event.url.pathname !== "/maintenance" && process.env.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);
}

View File

@ -1,6 +1,6 @@
<div class="max-w-[6rem] inline-block">
<a href="/users/1">
<img class="rounded-full border-2 border-grey-300 w-24 h-24 shadow-lg" src="" alt="" />
<p class="text-center">Rowblox</p>
<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">bucks</p>
</a>
</div>

View File

@ -28,17 +28,17 @@ async function hashPassword(plaintext) {
});
}
async function compareHash(plaintext, hash) {
export async function compareHash(plaintext, hash) {
return new Promise((resolve, reject) => {
compare(plaintext, hash, (err, result) => resolve(result));
});
}
async function genSession() {
function genSession() {
return randomBytes(64).toString("base64");
}
async function genInvite() {
function genInvite() {
return `${INVITE_KEY_PREFIX}${randomUUID()}`;
}
@ -49,7 +49,8 @@ export async function createUser(username, password, lastip) {
_id,
username,
password: await hashPassword(password),
lastip
lastip,
currency: 100
};
const avatar = {
@ -65,17 +66,55 @@ export async function createUser(username, password, lastip) {
Assets: {}
};
await avatars.insertOne(avatar);
return await users.insertOne(user);
await Promise.all([avatars.insertOne(avatar), await users.insertOne(user)]);
return _id;
}
export async function createSession(_id, lastip) {
const user = await users.findOne({ _id });
if (!user) throw new Error("That user doesn't exist");
if (!user) return false;
return await sessions.insertOne({
_id,
session: genSession(),
const session = genSession();
await sessions.insertOne({
_id: session,
owner: _id,
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;
}

View File

@ -0,0 +1,4 @@
/** @type {import('./$types').LayoutServerLoad} */
export function load({ locals }) {
return { user: locals.user };
}

View File

@ -1,6 +1,9 @@
<script>
import Alert from "$lib/components/Alert.svelte";
import Banner from "$lib/components/AdBanner.svelte";
/** @type {import('./$types').LayoutData} */
export let data;
</script>
<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="" />
100
</a>
<p class="mx-2 text-2xl">|</p>
<div class="ml-1 relative inline-block dropdown">
<div class="border-l-2 border-blue-500 pl-1.5 ml-1 relative inline-block dropdown">
<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">
<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>

View File

@ -1,6 +1,7 @@
<script>
import Game from "$lib/components/GameListed.svelte";
import Friend from "$lib/components/UserListed.svelte";
import { page } from "$app/stores";
</script>
<svelte:head>
@ -8,8 +9,8 @@
</svelte:head>
<div class="flex items-center">
<img class="rounded-full border-2 border-grey-300 w-36 h-36 mx-2 shadow-lg" src="" alt="" />
<p class="mx-2 font-bold text-3xl">Hello, Only16Characters!</p>
<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, {$page.data.user?.username}!</p>
</div>
<div class="my-2">

View File

@ -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, "/");
}
};

View File

@ -1,18 +1,35 @@
<script>
let username, passwd;
function submit() {}
/** @type {import('./$types').ActionData} */ export let form;
</script>
<svelte:head>
<title>Login - Rowblox</title>
</svelte:head>
<form on:submit|preventDefault={submit} class="max-w-lg mx-auto text-center text-white">
<img class="mx-auto" src="/favicon.png" alt="" />
<form method="POST" class="max-w-lg mx-auto text-center text-white">
<img class="mx-auto mb-2" src="/favicon.png" alt="" />
<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 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} />
<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"
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>
<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>

View File

@ -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" }
});
}

View File

@ -6,7 +6,7 @@ import { MIN_USERNAME_LENGTH, MAX_USERNAME_LENGTH, USERNAME_REGEX, MIN_PASSWORD_
export const actions = {
default: async ({ cookies, request, getClientAddress }) => {
const session = cookies.get(COOKIE_NAME);
if (session) return redirect(302, "/");
if (session) throw redirect(302, "/landing");
const data = await request.formData();
const username = data.get("username");
@ -36,9 +36,7 @@ export const actions = {
});
const user = await createUser(username, password, getClientAddress());
console.log(user);
cookies.set(COOKIE_NAME, "murder row");
return invalid(500, { message: "Dis shit does NOT WORK!" });
cookies.set(COOKIE_NAME, await createSession(user, getClientAddress()), { secure: !!process.env.PRODUCTION });
throw redirect(302, "/");
}
};

View File

@ -8,10 +8,10 @@
</svelte:head>
<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">
<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
type="username"
name="username"
@ -20,7 +20,7 @@
value={form?.username ?? ""}
/>
<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
type="password"
name="password"
@ -28,7 +28,7 @@
placeholder="Password"
/>
<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
type="password"
name="confirm_password"
@ -36,7 +36,7 @@
placeholder="Confirm Password"
/>
<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
type="text"
name="invite_key"