api keys & better validator #6
7 changed files with 107 additions and 36 deletions
|
@ -1,40 +1,48 @@
|
|||
import { error, redirect } from '@sveltejs/kit';
|
||||
import { getSession } from '$lib/server/database';
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import { getSession, getUserApiKey } from '$lib/server/database';
|
||||
import { COOKIE } from '$lib/config';
|
||||
|
||||
const PUBLIC_RESOURCES = [
|
||||
'/',
|
||||
'/api',
|
||||
'/api/auth/register',
|
||||
'/api/auth/login',
|
||||
'/terms',
|
||||
'/privacy'
|
||||
];
|
||||
|
||||
/** @type {import('@sveltejs/kit').Handle} */
|
||||
export async function handle({ event, resolve }) {
|
||||
const { cookies, locals } = event;
|
||||
const session = await getSession(cookies.get(COOKIE) || '');
|
||||
const { cookies, locals, request } = event;
|
||||
|
||||
if (session && session.user) {
|
||||
locals.user = {
|
||||
id: session.user.id,
|
||||
username: session.user.username,
|
||||
email: session.user.email,
|
||||
maxUploadMB: session.user.maxUploadMB,
|
||||
role: session.user.role
|
||||
};
|
||||
} else {
|
||||
if (event.route.id) {
|
||||
if (event.route.id.includes('(app)')) return redirect(303, '/');
|
||||
let cookie = cookies.get(COOKIE);
|
||||
let bearer = request.headers.get('Authorization');
|
||||
if (bearer) bearer = bearer.replace('Bearer ', '');
|
||||
|
||||
if (cookie) {
|
||||
const session = await getSession(cookie);
|
||||
if (session && session.user) {
|
||||
locals.user = {
|
||||
id: session.user.id,
|
||||
username: session.user.username,
|
||||
email: session.user.email,
|
||||
maxUploadMB: session.user.maxUploadMB,
|
||||
role: session.user.role
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (bearer && !locals.user) {
|
||||
const apiKey = await getUserApiKey(bearer);
|
||||
if (apiKey && apiKey.user) {
|
||||
locals.user = {
|
||||
id: apiKey.user.id,
|
||||
username: apiKey.user.username,
|
||||
email: apiKey.user.email,
|
||||
maxUploadMB: apiKey.user.maxUploadMB,
|
||||
role: apiKey.user.role
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (!locals.user && event.route.id) {
|
||||
if (event.route.id.includes('(app)')) return redirect(303, '/');
|
||||
}
|
||||
|
||||
return await resolve(event);
|
||||
}
|
||||
|
||||
/** @type {import('@sveltejs/kit').HandleServerError} */
|
||||
export async function handleError({ error, event, status, message }) {
|
||||
export async function handleError({ error, status, message }) {
|
||||
console.log(error);
|
||||
|
||||
return {
|
||||
|
|
|
@ -60,7 +60,7 @@
|
|||
type={'email'}
|
||||
name={'email'}
|
||||
id={'email'}
|
||||
placeholder={'user@example.com'}
|
||||
placeholder={'john@doefamily.com'}
|
||||
bind:value={email}
|
||||
required={true}
|
||||
>
|
||||
|
|
|
@ -80,7 +80,7 @@
|
|||
type={'email'}
|
||||
name={'email'}
|
||||
id={'email'}
|
||||
placeholder={'user@example.com'}
|
||||
placeholder={'jane@doefamily.com'}
|
||||
bind:value={email}
|
||||
required={true}
|
||||
>
|
||||
|
|
|
@ -11,8 +11,8 @@
|
|||
-->
|
||||
|
||||
{#if type === 'username'}
|
||||
<div class="flex p-2 space-x-1 rounded-lg shadow-md bg-crust">
|
||||
<div class="py-0.5 pr-1 border-r-2 border-overlay2">
|
||||
<div class="flex p-2 space-x-2 rounded-lg shadow-md bg-crust">
|
||||
<div class="py-0.5 pr-2 border-r-2 border-overlay0">
|
||||
<slot />
|
||||
</div>
|
||||
<input
|
||||
|
@ -26,8 +26,8 @@
|
|||
/>
|
||||
</div>
|
||||
{:else if type === 'email'}
|
||||
<div class="flex p-2 space-x-1 rounded-lg shadow-md bg-crust">
|
||||
<div class="py-0.5 pr-1 border-r-2 border-overlay2">
|
||||
<div class="flex p-2 space-x-2 rounded-lg shadow-md bg-crust">
|
||||
<div class="py-0.5 pr-2 border-r-2 border-overlay0">
|
||||
<slot />
|
||||
</div>
|
||||
<input
|
||||
|
@ -41,8 +41,8 @@
|
|||
/>
|
||||
</div>
|
||||
{:else if type === 'password'}
|
||||
<div class="flex p-2 space-x-1 rounded-lg shadow-md bg-crust">
|
||||
<div class="py-0.5 pr-1 border-r-2 border-overlay2">
|
||||
<div class="flex p-2 space-x-2 rounded-lg shadow-md bg-crust">
|
||||
<div class="py-0.5 pr-2 border-r-2 border-overlay0">
|
||||
<slot />
|
||||
</div>
|
||||
<input
|
||||
|
|
|
@ -172,3 +172,28 @@ export async function deleteUserApiKey(userId: number, id: string) {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
export async function getUserApiKey(id: string) {
|
||||
if (!id) return false;
|
||||
|
||||
return await prisma.aPIKey.findFirst({
|
||||
where: {
|
||||
id
|
||||
},
|
||||
include: {
|
||||
user: {
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
email: true,
|
||||
password: true,
|
||||
role: true,
|
||||
createdAt: true,
|
||||
lastSeen: true,
|
||||
maxUploadMB: true,
|
||||
settings: true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ const ApiKeyLimits = {
|
|||
setInterval(function resetMinute() {}, 1000 * 60);
|
||||
setInterval(function resetHour() {}, 1000 * 60 * 60);
|
||||
|
||||
function hash(input) {
|
||||
function hash(input: string) {
|
||||
createHash('sha256').update(input).digest('hex');
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import Button from '$lib/components/Inputs/Button.svelte';
|
||||
import { API_KEY_PERMISSIONS } from '$lib/config';
|
||||
import { get } from 'svelte/store';
|
||||
|
||||
let awesome = '';
|
||||
|
||||
async function click() {
|
||||
const response = await fetch('/api/v1/keys');
|
||||
const body = (await response.json()) as { id: string; permissions: number }[];
|
||||
|
||||
const key = body.find((key) => key.permissions & API_KEY_PERMISSIONS.CREATE_UPLOADS);
|
||||
if (!key) return (awesome = 'What the fuck did i tell you');
|
||||
|
||||
awesome = `{
|
||||
"Version": "14.0.0",
|
||||
"Name": "cirros file uploader",
|
||||
"DestinationType": "ImageUploader, FileUploader",
|
||||
"RequestMethod": "POST",
|
||||
"RequestURL": "${get(page).url.origin}/api/v1/upload",
|
||||
"Headers": {
|
||||
"Authorization": "Bearer ${key.id}",
|
||||
},
|
||||
"Body": "MultipartFormData",
|
||||
"FileFormName": "file",
|
||||
"URL": "${get(page).url.origin}{response}",
|
||||
}`;
|
||||
}
|
||||
</script>
|
||||
|
||||
<p>I'll make real documentation later but for now have this ShareX button</p>
|
||||
<p class="mb-2">
|
||||
MAKE SURE TO HAVE A VALID API KEY WITH THE CREATE_UPLOADS PERMISSION ( 1 << 0 )
|
||||
</p>
|
||||
<Button {click}>The sharex button in question</Button>
|
||||
|
||||
<tt class="block mt-3 whitespace-pre-wrap">{awesome}</tt>
|
Loading…
Reference in a new issue