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} */
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
/** @type {import('./$types').LayoutServerLoad} */
|
||||
export function load({ locals }) {
|
||||
return { user: locals.user };
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
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>
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
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, "/");
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Reference in New Issue