From 557423e887a363d0d1be1dfc6db613f83fd7cec0 Mon Sep 17 00:00:00 2001 From: mat Date: Thu, 17 Mar 2022 14:27:36 -0500 Subject: add logging in --- src/app.d.ts | 18 ++--- src/hooks.ts | 28 +++++++- src/lib/APITypes.d.ts | 30 +++++++- src/lib/LoginButton.svelte | 32 +++++++++ src/lib/api.ts | 2 +- src/routes/index.svelte | 21 +++++- src/routes/loggedin.ts | 8 ++- src/routes/logout.ts | 34 +++++++++ src/routes/player/[player]/[profile].svelte | 2 +- src/routes/player/index.ts | 4 +- src/routes/profile.svelte | 79 +++++++++++++++++++++ src/routes/verify.svelte | 63 +++++++++++++++++ src/routes/verify.ts | 104 ++++++++++++++++++++++++++++ 13 files changed, 404 insertions(+), 21 deletions(-) create mode 100644 src/lib/LoginButton.svelte create mode 100644 src/routes/logout.ts create mode 100644 src/routes/profile.svelte create mode 100644 src/routes/verify.svelte create mode 100644 src/routes/verify.ts (limited to 'src') diff --git a/src/app.d.ts b/src/app.d.ts index efdb902..ddaae39 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -2,14 +2,16 @@ // // See https://kit.svelte.dev/docs/typescript // // for information about these interfaces -// declare namespace App { -// interface Locals { -// userid: string; -// } +declare namespace App { + interface Locals { + sid: string | undefined + } -// interface Platform {} + // interface Platform {} -// interface Session {} + interface Session { + sid: string | undefined + } -// interface Stuff {} -// } + // interface Stuff {} +} diff --git a/src/hooks.ts b/src/hooks.ts index 9cd6ca4..5b3464c 100644 --- a/src/hooks.ts +++ b/src/hooks.ts @@ -1,11 +1,16 @@ import cookie from 'cookie' -import { v4 as uuid } from '@lukeed/uuid' -import type { Handle } from '@sveltejs/kit' +import type { ExternalFetch, GetSession, Handle } from '@sveltejs/kit' + +const SKYBLOCK_STATS_API_KEY = process.env.SKYBLOCK_STATS_API_KEY +if (!SKYBLOCK_STATS_API_KEY) + console.warn('SKYBLOCK_STATS_API_KEY is not set as an environment variable. This is required for logging in with Skyblock Stats to work. It should be the same as the `key` environment variable in skyblock-api.') export const handle: Handle = async ({ event, resolve }) => { - // const cookies = cookie.parse(event.request.headers.get('cookie') || ''); + const cookies = cookie.parse(event.request.headers.get('cookie') || '') // event.locals.userid = cookies.userid || uuid(); + event.locals.sid = cookies.sid + const response = await resolve(event) // if (!cookies.userid) { @@ -22,3 +27,20 @@ export const handle: Handle = async ({ event, resolve }) => { return response } + +export const getSession: GetSession = async ({ locals }) => { + return { + sid: locals.sid + } +} + +export const externalFetch: ExternalFetch = async (request) => { + if (SKYBLOCK_STATS_API_KEY && request.url.startsWith('https://skyblock-api.matdoes.dev/')) { + // add the key as a header + request.headers.set('key', SKYBLOCK_STATS_API_KEY) + } + + console.log('request', request.url) + + return fetch(request) +} \ No newline at end of file diff --git a/src/lib/APITypes.d.ts b/src/lib/APITypes.d.ts index 3a54cea..b0deee0 100644 --- a/src/lib/APITypes.d.ts +++ b/src/lib/APITypes.d.ts @@ -162,4 +162,32 @@ export interface ElectionData { year: number candidates: Candidate[] } | null -} \ No newline at end of file +} + +interface SessionSchema { + _id?: string + refresh_token: string + discord_user: { + id: string + name: string + } + lastUpdated: Date +} + +export interface AccountCustomization { + backgroundUrl?: string + pack?: string + emoji?: string +} + +export interface AccountSchema { + _id?: string + discordId: string + minecraftUuid?: string + customization?: AccountCustomization +} + +export interface CleanSocialMedia { + discord: string | null + forums: string | null +} diff --git a/src/lib/LoginButton.svelte b/src/lib/LoginButton.svelte new file mode 100644 index 0000000..bba078e --- /dev/null +++ b/src/lib/LoginButton.svelte @@ -0,0 +1,32 @@ + + +
+ {#if loggedIn} + + {:else} + + + + {/if} +
+ + diff --git a/src/lib/api.ts b/src/lib/api.ts index db6bf81..243cf2b 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -1 +1 @@ -export const API_URL = 'https://skyblock-api.matdoes.dev/' \ No newline at end of file +export const API_URL = 'https://skyblock-api.matdoes.dev/' diff --git a/src/routes/index.svelte b/src/routes/index.svelte index 6f6bae1..54843bd 100644 --- a/src/routes/index.svelte +++ b/src/routes/index.svelte @@ -1,13 +1,28 @@ + + @@ -20,6 +35,7 @@ />
+

SkyBlock Stats

@@ -56,7 +72,6 @@

Info

Website made by mat.

-

Join the Forum Sweats Discord server.

Resource packs: PacksHQ (default), Furfsky, diff --git a/src/routes/loggedin.ts b/src/routes/loggedin.ts index eb7236d..9eadf67 100644 --- a/src/routes/loggedin.ts +++ b/src/routes/loggedin.ts @@ -2,15 +2,17 @@ import { API_URL } from '$lib/api' import type { RequestHandler } from '@sveltejs/kit' import type { JSONValue } from '@sveltejs/kit/types/internal' -export const get: RequestHandler = async ({ params }) => { - const code = params.code +export const get: RequestHandler = async ({ url }) => { + const code = url.searchParams.get('code') + const redirectUri = `${url.protocol}//${url.host}/loggedin` const response = await fetch(`${API_URL}accounts/createsession`, { method: 'POST', headers: { 'content-type': 'application/json', }, body: JSON.stringify({ - code + code, + redirectUri: redirectUri }), }).then(res => { if (res.status !== 200) diff --git a/src/routes/logout.ts b/src/routes/logout.ts new file mode 100644 index 0000000..f2f5e33 --- /dev/null +++ b/src/routes/logout.ts @@ -0,0 +1,34 @@ +import { API_URL } from '$lib/api' +import type { RequestHandler } from '@sveltejs/kit' + +const DISCORD_CLIENT_ID = process.env.DISCORD_CLIENT_ID +if (!DISCORD_CLIENT_ID) + console.warn('DISCORD_CLIENT_ID is not set as an environment variable. This is required for logging in with Discord to work.') + +export const get: RequestHandler = async ({ request, params, locals, url }) => { + // if the sid is wrong, nothing to do + console.log(url.searchParams.get('sid'), locals.sid) + if (url.searchParams.has('sid') && url.searchParams.get('sid') === locals.sid) { + console.log('ok sent logout') + await fetch(`${API_URL}accounts/session`, { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + uuid: locals.sid + }), + }).then(res => { + if (res.status !== 200) + throw new Error(res.statusText) + }) + } + return { + status: 303, + headers: { + location: '/', + 'Set-Cookie': 'sid=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/;' + } + } + +} diff --git a/src/routes/player/[player]/[profile].svelte b/src/routes/player/[player]/[profile].svelte index b87741c..02c46bb 100644 --- a/src/routes/player/[player]/[profile].svelte +++ b/src/routes/player/[player]/[profile].svelte @@ -39,7 +39,7 @@ pack = (await import('skyblock-assets/matchers/worlds_and_beyond.json')) as any break default: - // packshq is the default pack + // furfsky reborn is the default pack pack = (await import('skyblock-assets/matchers/furfsky_reborn.json')) as any break } diff --git a/src/routes/player/index.ts b/src/routes/player/index.ts index c9d1b90..4644499 100644 --- a/src/routes/player/index.ts +++ b/src/routes/player/index.ts @@ -1,4 +1,6 @@ -export async function post({ request }) { +import type { RequestHandler } from '@sveltejs/kit' + +export const post: RequestHandler = async ({ request }) => { const form = await request.formData() const player = form.get('user-search') diff --git a/src/routes/profile.svelte b/src/routes/profile.svelte new file mode 100644 index 0000000..c88b8fe --- /dev/null +++ b/src/routes/profile.svelte @@ -0,0 +1,79 @@ + + + + + +

+ +
+ {#if session && session._id} + + {/if} +

Customize Profile

+ + {#if player && player.player} + View profile + {:else} +

No linked Minecraft account

+ {/if} +

+ Pack: +

+
+ + diff --git a/src/routes/verify.svelte b/src/routes/verify.svelte new file mode 100644 index 0000000..e900a5f --- /dev/null +++ b/src/routes/verify.svelte @@ -0,0 +1,63 @@ + + + + + +
+ +
+

Verify Minecraft account

+

Please enter your Minecraft username to verify that this is your account.

+

This will check with the Hypixel API that your Discord username matches your Hypixel name.

+ {#if errorCode && errorCode in errorCodes} +
+ + {errorCodes[errorCode]} +
+ {/if} +
+ + +
+
+ + diff --git a/src/routes/verify.ts b/src/routes/verify.ts new file mode 100644 index 0000000..5951314 --- /dev/null +++ b/src/routes/verify.ts @@ -0,0 +1,104 @@ +import { API_URL } from '$lib/api' +import type { AccountSchema, CleanUser, SessionSchema } from '$lib/APITypes' +import type { RequestHandler } from '@sveltejs/kit' + +function redirect(status: number, location: string) { + return { + status, + headers: { + location, + }, + } +} + +export const post: RequestHandler = async ({ request, locals }) => { + console.log('ok!') + if (!process.env.SKYBLOCK_STATS_API_KEY) { + return redirect(303, `/verify?error=NO_KEY`) + } + console.log('sid check') + if (locals.sid === undefined) { + // return { + // status: 303, + // redirect: '/login', + // } + return redirect(303, '/login') + } + + const form = await request.formData() + + // username or uuid + const playerIdentifier = form.get('ign') + console.log('ign check') + if (!playerIdentifier) { + // return { + // status: 303, + // redirect: '/verify?error=NO_IGN', + // } + return redirect(303, `/verify?error=NO_IGN`) + } + + const playerResponse: CleanUser = await fetch(`${API_URL}player/${playerIdentifier}`).then(res => res.json()) + const sessionResponse: { session: SessionSchema | null, account: AccountSchema | null } = await fetch(`${API_URL}accounts/session`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + uuid: locals.sid, + }), + }).then(r => r.json()) + + console.log('session check') + if (!sessionResponse.session) + // return { + // status: 303, + // redirect: '/login', + // } + return redirect(303, '/login') + + const hypixelDiscordName = playerResponse.player?.socials.discord + + console.log('discord check') + if (!hypixelDiscordName) + // return { + // status: 303, + // redirect: '/verify?error=NOT_LINKED', + // } + return redirect(303, `/verify?error=NOT_LINKED`) + + + const discordUser = sessionResponse.session.discord_user + const actualDiscordName = discordUser.name + // some people link themselves as # instead of # + const actualDiscordIdDiscrim = `${discordUser.id}#${discordUser.name.split('#')[1]}` + + console.log('name check') + if (!(hypixelDiscordName === actualDiscordName || hypixelDiscordName === actualDiscordIdDiscrim)) + // return { + // status: 303, + // redirect: `/verify?error=WRONG_NAME?current=${hypixelDiscordName}&correct=${actualDiscordName}`, + // } + return redirect(303, `/verify?error=WRONG_NAME¤t=${encodeURIComponent(hypixelDiscordName)}&correct=${encodeURIComponent(actualDiscordName)}`) + + const updatedAccount: AccountSchema = { + discordId: sessionResponse.session.discord_user.id, + minecraftUuid: playerResponse.player?.uuid + } + + await fetch(`${API_URL}accounts/update`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + key: process.env.SKYBLOCK_STATS_API_KEY + }, + body: JSON.stringify(updatedAccount), + }).then(r => r.json()) + + console.log('epic') + // return { + // status: 303, + // redirect: '/profile' + // } + return redirect(303, '/profile') +} \ No newline at end of file -- cgit