wip
Rust / Run tests (push) Failing after 58s Details

This commit is contained in:
DataHearth 2023-08-11 19:45:45 +02:00
parent ca59f72429
commit 3ba965bb24
No known key found for this signature in database
GPG Key ID: E88FD356ACC5F3C4
9 changed files with 179 additions and 108 deletions

View File

@ -1,6 +1,6 @@
{
"name": "frontend",
"version": "0.0.1",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "vite dev",
@ -19,12 +19,14 @@
"@tailwindcss/forms": "^0.5.4",
"@tailwindcss/typography": "^0.5.9",
"@types/chart.js": "^2.9.37",
"@types/cookie": "^0.5.1",
"@types/uuid": "^9.0.2",
"@typescript-eslint/eslint-plugin": "^5.45.0",
"@typescript-eslint/parser": "^5.45.0",
"@urql/svelte": "^4.0.3",
"autoprefixer": "^10.4.14",
"chart.js": "^4.3.0",
"cookie": "^0.5.0",
"daisyui": "^3.2.1",
"dotenv": "^16.3.1",
"eslint": "^8.28.0",

View File

@ -26,6 +26,9 @@ devDependencies:
'@types/chart.js':
specifier: ^2.9.37
version: 2.9.37
'@types/cookie':
specifier: ^0.5.1
version: 0.5.1
'@types/uuid':
specifier: ^9.0.2
version: 9.0.2
@ -44,6 +47,9 @@ devDependencies:
chart.js:
specifier: ^4.3.0
version: 4.3.0
cookie:
specifier: ^0.5.0
version: 0.5.0
daisyui:
specifier: ^3.2.1
version: 3.2.1

13
frontend/src/app.d.ts vendored
View File

@ -2,15 +2,18 @@
// for information about these interfaces
declare global {
namespace App {
// interface Error {
// status: number;
// message: string;
// }
// interface Error {}
interface Locals {
session: boolean;
}
// interface PageData {}
interface PageData {
user?: {
id: string;
username: string;
avatar: string;
};
}
// interface Platform {}
}
}

View File

@ -7,6 +7,6 @@
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover" data-theme="light">
%sveltekit.body%
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>

View File

@ -1,42 +1,29 @@
import { env } from '$env/dynamic/private';
import type { OAuth2Response } from '$lib/types';
import { error, redirect, type Handle } from '@sveltejs/kit';
import { redirect, type Handle } from '@sveltejs/kit';
import { sequence } from '@sveltejs/kit/hooks';
const protectedRoutes = ['/'];
const auth: Handle = async ({ resolve, event }) => {
console.log(`handle auth for route: ${event.url.pathname}`);
const refreshToken = event.cookies.get('refresh-token');
const accessToken = event.cookies.get('access-token');
let accessToken = event.cookies.get('access-token');
console.log(`refresh token: ${refreshToken}`);
console.log(`access token: ${accessToken}`);
if (!accessToken && refreshToken) {
const rsp = await fetch('https://discord.com/api/oauth2/token', {
method: 'POST',
const rsp = await event.fetch('/auth/discord', {
method: 'PUT',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
'Content-Type': 'application/json'
},
body: new URLSearchParams({
client_id: env.CLIENT_ID,
client_secret: env.CLIENT_SECRET,
grant_type: 'refresh_token',
refresh_token: refreshToken
})
body: JSON.stringify({ refreshToken })
});
if (!rsp.ok) {
console.error(`failed to refresh token: ${rsp.status}`);
event.cookies.delete('refresh-token');
throw redirect(303, '/login');
}
if (!rsp.ok) throw redirect(302, '/login');
const { access_token, expires_in }: OAuth2Response = await rsp.json();
event.cookies.set('access-token', access_token, {
maxAge: expires_in,
expires: new Date(Date.now() + expires_in),
httpOnly: true,
sameSite: true,
path: '/'
});
accessToken = event.cookies.get('access-token');
}
// * grab the access token again, in case it was just refreshed
@ -45,81 +32,19 @@ const auth: Handle = async ({ resolve, event }) => {
return await resolve(event);
};
const handleAuth: Handle = async ({ resolve, event }) => {
if (event.locals.session && event.url.pathname.includes('/auth')) throw redirect(303, '/');
else if (event.locals.session && event.url.pathname === '/logout') {
event.cookies.delete('access-token');
event.cookies.delete('refresh-token');
throw redirect(303, '/login');
} else if (event.locals.session) return await resolve(event);
if (event.url.origin !== env.ORIGIN) {
console.error(`invalid origin. ${event.url.origin}`);
throw error(403, 'invalid origin');
}
if (event.url.pathname === '/auth/discord') {
const params = new URLSearchParams({
client_id: env.CLIENT_ID,
redirect_uri: `${env.ORIGIN}/auth/callback/discord`,
response_type: 'code',
scope: 'identify'
});
throw redirect(302, `https://discord.com/api/oauth2/authorize?${params.toString()}`);
} else if (event.url.pathname === '/auth/callback/discord') {
const code = event.url.searchParams.get('code');
if (!code) {
console.error(`failed to get code in callback url: ${event.url}`);
throw redirect(303, '/login');
}
const rsp = await fetch('https://discord.com/api/oauth2/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
client_id: env.CLIENT_ID,
client_secret: env.CLIENT_SECRET,
grant_type: 'authorization_code',
redirect_uri: `${env.ORIGIN}/auth/callback/discord`,
code
})
});
if (!rsp.ok) throw redirect(303, '/login');
const { access_token, refresh_token, expires_in }: OAuth2Response = await rsp.json();
event.cookies.set('access-token', access_token, {
maxAge: expires_in,
expires: new Date(Date.now() + expires_in),
httpOnly: true,
sameSite: true,
path: '/'
});
event.cookies.set('refresh-token', refresh_token, {
maxAge: Date.now() + 60 * 60 * 24 * 30,
expires: new Date(Date.now() + 60 * 60 * 24 * 30),
httpOnly: true,
sameSite: true,
path: '/'
});
console.info('successfully authenticated user');
throw redirect(303, '/');
}
return await resolve(event);
};
const guard: Handle = async ({ resolve, event }) => {
if (protectedRoutes.includes(event.url.pathname) && !event.locals.session) {
console.warn(`authentication failed for: ${event.url.pathname}`);
throw redirect(303, '/login');
} else if (event.url.pathname === '/login' && event.locals.session) throw redirect(303, '/');
throw redirect(302, '/login');
} else if (
(event.url.pathname === '/login' || event.url.pathname.includes('/auth')) &&
event.locals.session
) {
console.log('already authenticated. redirecting to home page');
throw redirect(302, '/');
}
return await resolve(event);
};
export const handle: Handle = sequence(auth, guard, handleAuth);
export const handle: Handle = sequence(auth, guard);

View File

@ -7,6 +7,9 @@
import type { PageData } from './$types';
export let data: PageData;
$: ({ username, avatar, id } = data);
setContextClient(
new Client({
url: env.PUBLIC_GRAPHQL_ENDPOINT,
@ -24,13 +27,13 @@
</div>
<div class="flex-1 justify-end space-x-5 mr-5">
{#if data.id && data.avatar && data.username}
{#if id && avatar && username}
<div class="tooltip tooltip-bottom tooltip-info" data-tip="Copy ID">
<button
class="btn btn-ghost p-0 normal-case"
on:click={() => navigator.clipboard.writeText(data.id)}
on:click={() => navigator.clipboard.writeText(id)}
>
{data.username}
{username}
</button>
</div>
<div class="tooltip tooltip-bottom tooltip-info" data-tip="Logout">
@ -40,7 +43,7 @@
</div>
<div class="avatar">
<div class="w-8 rounded-full">
<img src={data.avatar} alt="user's avatar" />
<img src={avatar} alt="user's avatar" />
</div>
</div>
{/if}

View File

@ -0,0 +1,62 @@
import { env } from '$env/dynamic/private';
import { env as publicEnv } from '$env/dynamic/public';
import type { OAuth2Response } from '$lib/types';
import { redirect } from '@sveltejs/kit';
import { serialize } from 'cookie';
import type { RequestHandler } from './$types';
export const GET: RequestHandler = async (event) => {
const code = event.url.searchParams.get('code');
if (!code) {
console.error(`failed to get code in callback url: ${event.url}`);
throw redirect(302, '/login');
}
const rsp = await fetch('https://discord.com/api/oauth2/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
client_id: env.CLIENT_ID,
client_secret: env.CLIENT_SECRET,
grant_type: 'authorization_code',
redirect_uri: `${publicEnv.PUBLIC_ORIGIN}/auth/callback/discord`,
code
})
});
if (!rsp.ok) throw redirect(302, '/login');
const { access_token, refresh_token, expires_in }: OAuth2Response = await rsp.json();
const headers = new Headers();
headers.set('Location', '/');
headers.append(
'Set-Cookie',
serialize('access-token', access_token, {
domain: publicEnv.PUBLIC_ORIGIN,
maxAge: expires_in,
expires: new Date(Date.now() + expires_in),
httpOnly: true,
sameSite: true,
path: '/'
})
);
headers.append(
'Set-Cookie',
serialize('refresh-token', refresh_token, {
domain: publicEnv.PUBLIC_ORIGIN,
maxAge: 60 * 60 * 24 * 7,
expires: new Date(Date.now() + 60 * 60 * 24 * 7),
httpOnly: true,
sameSite: true,
path: '/'
})
);
return new Response(null, {
status: 302,
headers
});
};

View File

@ -0,0 +1,61 @@
import { env } from '$env/dynamic/private';
import { env as publicEnv } from '$env/dynamic/public';
import type { OAuth2Response } from '$lib/types';
import { serialize } from 'cookie';
import type { RequestHandler } from './$types';
export const GET: RequestHandler = () => {
const params = new URLSearchParams({
client_id: env.CLIENT_ID,
redirect_uri: `${publicEnv.PUBLIC_ORIGIN}/auth/callback/discord`,
response_type: 'code',
scope: 'identify'
});
return new Response(null, {
headers: {
Location: `https://discord.com/api/oauth2/authorize?${params.toString()}`
},
status: 302
});
};
export const PUT: RequestHandler = async ({ request, fetch }) => {
const { refreshToken } = await request.json();
const rsp = await fetch('https://discord.com/api/oauth2/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
client_id: env.CLIENT_ID,
client_secret: env.CLIENT_SECRET,
grant_type: 'refresh_token',
refresh_token: refreshToken
})
});
if (!rsp.ok) return new Response(null, { status: 401 });
const { access_token, expires_in }: OAuth2Response = await rsp.json();
const headers = new Headers();
headers.set('Location', '/');
headers.append(
'Set-Cookie',
serialize('access-token', access_token, {
domain: publicEnv.PUBLIC_ORIGIN,
maxAge: expires_in,
expires: new Date(Date.now() + expires_in),
httpOnly: true,
sameSite: true,
path: '/'
})
);
return new Response(null, {
status: 302,
headers
});
};

View File

@ -0,0 +1,9 @@
import { redirect } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
export const GET: RequestHandler = async (event) => {
event.cookies.delete('access-token');
event.cookies.delete('refresh-token');
throw redirect(302, '/login');
};