yap #1
14 changed files with 438 additions and 279 deletions
|
@ -1,9 +1,5 @@
|
||||||
AUTH_SECRET=
|
DATABASE_URL=
|
||||||
|
|
||||||
MYSQL_HOST=
|
|
||||||
MYSQL_USERNAME=
|
|
||||||
MYSQL_PASSWORD=
|
|
||||||
|
|
||||||
KEYCLOAK_CLIENT_ID=
|
KEYCLOAK_CLIENT_ID=
|
||||||
KEYCLOAK_CLIENT_SECRET=
|
KEYCLOAK_CLIENT_SECRET=
|
||||||
KEYCLOAK_CLIENT_ISSUER=
|
KEYCLOAK_ISSUER=
|
|
@ -15,7 +15,7 @@
|
||||||
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
||||||
"autoprefixer": "^10.4.18",
|
"autoprefixer": "^10.4.18",
|
||||||
"postcss": "^8.4.35",
|
"postcss": "^8.4.35",
|
||||||
"prettier": "^3.1.1",
|
"prettier": "^3.2.5",
|
||||||
"prettier-plugin-svelte": "^3.1.2",
|
"prettier-plugin-svelte": "^3.1.2",
|
||||||
"prisma": "^5.11.0",
|
"prisma": "^5.11.0",
|
||||||
"svelte": "^4.2.7",
|
"svelte": "^4.2.7",
|
||||||
|
|
|
@ -1,10 +1,23 @@
|
||||||
<script>
|
<script>
|
||||||
export let click;
|
export let click,
|
||||||
|
disabled = false,
|
||||||
|
pulse = false;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<button type="button" on:click={click}>
|
<button
|
||||||
|
class="group/button {pulse ? 'animate-pulse cursor-wait' : ''} transition-colors"
|
||||||
|
type="button"
|
||||||
|
on:click={click}
|
||||||
|
{disabled}
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
class="flex p-2 space-x-2 rounded-lg border-b-2 transition-colors border-neutral-400 hover:border-neutral-500 hover:dark:border-neutral-500 dark:border-neutral-700 bg-neutral-200 dark:bg-neutral-900"
|
class="flex space-x-2 rounded-lg border-b-2 p-2 transition-colors
|
||||||
|
|
||||||
|
border-neutral-400 bg-neutral-200
|
||||||
|
hover:border-neutral-500 hover:group-disabled/button:border-neutral-400
|
||||||
|
|
||||||
|
dark:border-neutral-700 dark:bg-neutral-900
|
||||||
|
hover:dark:border-neutral-500 hover:group-disabled/button:dark:border-neutral-700"
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
|
|
6
src/lib/components/ButtonIcon.svelte
Normal file
6
src/lib/components/ButtonIcon.svelte
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<div
|
||||||
|
class="group-disabled/button:text-neutral-500/50
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</div>
|
6
src/lib/components/ButtonText.svelte
Normal file
6
src/lib/components/ButtonText.svelte
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<p
|
||||||
|
class="group-disabled/button:text-neutral-500/50
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</p>
|
|
@ -1,16 +1,78 @@
|
||||||
<script>
|
<script>
|
||||||
export let type, name, id, placeholder;
|
import { CircleAlert, Check } from 'lucide-svelte';
|
||||||
|
|
||||||
|
export let type, name, id, placeholder, bind, required;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- insane that i have to do this because -->
|
||||||
|
<!-- 'type' attribute cannot be dynamic if input uses two-way binding -->
|
||||||
|
{#if type === 'username'}
|
||||||
<div class="flex p-2 space-x-1 rounded-lg transition-colors bg-neutral-200 dark:bg-neutral-900">
|
<div class="flex p-2 space-x-1 rounded-lg transition-colors bg-neutral-200 dark:bg-neutral-900">
|
||||||
<div class="py-0.5 pr-1 border-r-2 transition-colors border-neutral-400 dark:border-neutral-700">
|
<div
|
||||||
|
class="py-0.5 pr-1 border-r-2 transition-colors border-neutral-400 dark:border-neutral-700"
|
||||||
|
>
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
class="py-0.5 transition-colors bg-neutral-200 dark:bg-neutral-900"
|
class="py-0.5 transition-colors bg-neutral-200 dark:bg-neutral-900 peer"
|
||||||
{type}
|
type="username"
|
||||||
{name}
|
{name}
|
||||||
{id}
|
{id}
|
||||||
{placeholder}
|
{placeholder}
|
||||||
|
{required}
|
||||||
|
bind:value={bind}
|
||||||
/>
|
/>
|
||||||
|
<div class="peer-invalid:flex hidden my-auto">
|
||||||
|
<CircleAlert />
|
||||||
</div>
|
</div>
|
||||||
|
<div class="peer-invalid:hidden my-auto">
|
||||||
|
<Check />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{:else if type === 'email'}
|
||||||
|
<div class="flex p-2 space-x-1 rounded-lg transition-colors bg-neutral-200 dark:bg-neutral-900">
|
||||||
|
<div
|
||||||
|
class="py-0.5 pr-1 border-r-2 transition-colors border-neutral-400 dark:border-neutral-700"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
class="py-0.5 transition-colors bg-neutral-200 dark:bg-neutral-900 peer"
|
||||||
|
type="email"
|
||||||
|
{name}
|
||||||
|
{id}
|
||||||
|
{placeholder}
|
||||||
|
{required}
|
||||||
|
bind:value={bind}
|
||||||
|
/>
|
||||||
|
<div class="peer-invalid:flex hidden my-auto">
|
||||||
|
<CircleAlert />
|
||||||
|
</div>
|
||||||
|
<div class="peer-invalid:hidden my-auto">
|
||||||
|
<Check />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{:else if type === 'password'}
|
||||||
|
<div class="flex p-2 space-x-1 rounded-lg transition-colors bg-neutral-200 dark:bg-neutral-900">
|
||||||
|
<div
|
||||||
|
class="py-0.5 pr-1 border-r-2 transition-colors border-neutral-400 dark:border-neutral-700"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
class="py-0.5 transition-colors bg-neutral-200 dark:bg-neutral-900 peer"
|
||||||
|
type="password"
|
||||||
|
{name}
|
||||||
|
{id}
|
||||||
|
{placeholder}
|
||||||
|
{required}
|
||||||
|
bind:value={bind}
|
||||||
|
/>
|
||||||
|
<div class="peer-invalid:flex hidden my-auto">
|
||||||
|
<CircleAlert />
|
||||||
|
</div>
|
||||||
|
<div class="peer-invalid:hidden my-auto">
|
||||||
|
<Check />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
<div class="transition-colors fill-black dark:fill-white">
|
<div class="transition-colors fill-black dark:fill-white">
|
||||||
<Logo />
|
<Logo />
|
||||||
</div>
|
</div>
|
||||||
<form action="">
|
<form on:submit|preventDefault>
|
||||||
<div class="flex flex-col space-y-2">
|
<div class="flex flex-col space-y-2">
|
||||||
<FormInput type={'email'} name={'email'} id={'email'} placeholder={'user@example.com'}>
|
<FormInput type={'email'} name={'email'} id={'email'} placeholder={'user@example.com'}>
|
||||||
<Mail />
|
<Mail />
|
||||||
|
|
|
@ -1,12 +1,27 @@
|
||||||
<script>
|
<script>
|
||||||
import { blur } from 'svelte/transition';
|
import { blur } from 'svelte/transition';
|
||||||
import { Mail, SquareAsterisk, Undo, User, UserPlus } from 'lucide-svelte';
|
import { Mail, SquareAsterisk, Undo, User, UserPlus, Dot } from 'lucide-svelte';
|
||||||
|
import { toast } from 'svelte-sonner';
|
||||||
|
|
||||||
import Logo from '$lib/components/Logo.svelte';
|
import Logo from '$lib/components/Logo.svelte';
|
||||||
import FormInput from '$lib/components/FormInput.svelte';
|
import FormInput from '$lib/components/FormInput.svelte';
|
||||||
import Button from '$lib/components/Button.svelte';
|
import Button from '$lib/components/Button.svelte';
|
||||||
|
import ButtonText from '$lib/components/ButtonText.svelte';
|
||||||
|
import ButtonIcon from '$lib/components/ButtonIcon.svelte';
|
||||||
|
|
||||||
export let callback;
|
export let callback;
|
||||||
|
|
||||||
|
let disabled = false;
|
||||||
|
let username, email, password, cpassword;
|
||||||
|
|
||||||
|
async function register() {
|
||||||
|
disabled = true;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
toast('Failed to register.');
|
||||||
|
disabled = false;
|
||||||
|
}, 5_000);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div in:blur={{ amount: 1 }} class="flex justify-center items-center h-screen">
|
<div in:blur={{ amount: 1 }} class="flex justify-center items-center h-screen">
|
||||||
|
@ -14,15 +29,36 @@
|
||||||
<div class="transition-colors fill-black dark:fill-white">
|
<div class="transition-colors fill-black dark:fill-white">
|
||||||
<Logo />
|
<Logo />
|
||||||
</div>
|
</div>
|
||||||
<form action="">
|
<form on:submit|preventDefault>
|
||||||
<div class="flex flex-col space-y-2">
|
<div class="flex flex-col space-y-2">
|
||||||
<FormInput type={'username'} name={'username'} id={'username'} placeholder={'Username'}>
|
<FormInput
|
||||||
|
type={'username'}
|
||||||
|
name={'username'}
|
||||||
|
id={'username'}
|
||||||
|
placeholder={'Username'}
|
||||||
|
bind={username}
|
||||||
|
required={true}
|
||||||
|
>
|
||||||
<User />
|
<User />
|
||||||
</FormInput>
|
</FormInput>
|
||||||
<FormInput type={'email'} name={'email'} id={'email'} placeholder={'user@example.com'}>
|
<FormInput
|
||||||
|
type={'email'}
|
||||||
|
name={'email'}
|
||||||
|
id={'email'}
|
||||||
|
placeholder={'user@example.com'}
|
||||||
|
bind={email}
|
||||||
|
required={true}
|
||||||
|
>
|
||||||
<Mail />
|
<Mail />
|
||||||
</FormInput>
|
</FormInput>
|
||||||
<FormInput type={'password'} name={'password'} id={'password'} placeholder={'•'.repeat(16)}>
|
<FormInput
|
||||||
|
type={'password'}
|
||||||
|
name={'password'}
|
||||||
|
id={'password'}
|
||||||
|
placeholder={'•'.repeat(16)}
|
||||||
|
bind={password}
|
||||||
|
required={true}
|
||||||
|
>
|
||||||
<SquareAsterisk />
|
<SquareAsterisk />
|
||||||
</FormInput>
|
</FormInput>
|
||||||
<FormInput
|
<FormInput
|
||||||
|
@ -30,20 +66,22 @@
|
||||||
name={'cpassword'}
|
name={'cpassword'}
|
||||||
id={'cpassword'}
|
id={'cpassword'}
|
||||||
placeholder={'•'.repeat(16)}
|
placeholder={'•'.repeat(16)}
|
||||||
|
bind={cpassword}
|
||||||
|
required={true}
|
||||||
>
|
>
|
||||||
<SquareAsterisk />
|
<SquareAsterisk />
|
||||||
</FormInput>
|
</FormInput>
|
||||||
<div class="flex place-content-between">
|
<div class="flex place-content-between">
|
||||||
<Button click={callback}>
|
<Button click={callback} {disabled}>
|
||||||
<Undo />
|
<ButtonIcon><Undo /></ButtonIcon>
|
||||||
<p>Go Back</p>
|
<ButtonText>Go Back</ButtonText>
|
||||||
</Button>
|
</Button>
|
||||||
<Button>
|
<Button click={register} {disabled} pulse={disabled}>
|
||||||
<UserPlus />
|
<ButtonIcon><UserPlus /></ButtonIcon>
|
||||||
<p>Register</p>
|
<ButtonText>Register</ButtonText>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-xs text-neutral-500/50 dark:text-white/30">
|
<p class="text-xs text-center text-neutral-500/50 dark:text-white/30">
|
||||||
By registering an account you agree to the <br />
|
By registering an account you agree to the <br />
|
||||||
<a class="underline" href="/terms" data-sveltekit-reload>Terms of Service</a> and
|
<a class="underline" href="/terms" data-sveltekit-reload>Terms of Service</a> and
|
||||||
<a class="underline" href="/privacy" data-sveltekit-reload>Privacy Policy</a>.
|
<a class="underline" href="/privacy" data-sveltekit-reload>Privacy Policy</a>.
|
||||||
|
|
1
src/lib/config.js
Normal file
1
src/lib/config.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export const COOKIE = '.FILE-UPLOADER-SESSION';
|
30
src/lib/server/ratelimit.js
Normal file
30
src/lib/server/ratelimit.js
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import { createHash } from 'node:crypto';
|
||||||
|
import { COOKIE } from '$lib/config.js';
|
||||||
|
|
||||||
|
const limits = new Map();
|
||||||
|
|
||||||
|
const CookieLimits = {
|
||||||
|
Minute: 100,
|
||||||
|
Hour: 1000
|
||||||
|
};
|
||||||
|
|
||||||
|
const ApiKeyLimits = {
|
||||||
|
Minute: 0,
|
||||||
|
Hour: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
setInterval(function resetMinute() {}, 1000 * 60);
|
||||||
|
setInterval(function resetHour() {}, 1000 * 60 * 60);
|
||||||
|
|
||||||
|
function hash(input) {
|
||||||
|
createHash('sha256').update(input).digest('hex');
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function cookie({ cookies }) {
|
||||||
|
const hashed = hash(cookies.get(COOKIE));
|
||||||
|
console.log(hashed);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function apiKey(event) {}
|
||||||
|
|
||||||
|
export async function handle(event) {}
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
<PageMeta title="cirro's file uploader" />
|
<PageMeta title="cirro's file uploader" />
|
||||||
<ThemeHandler />
|
<ThemeHandler />
|
||||||
<Toaster theme={$darkMode ? 'dark' : 'light'} />
|
<Toaster theme={$darkMode ? 'dark' : 'light'} position={'bottom-center'} />
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<slot />
|
<slot />
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $state === 'landing'}
|
{#if $state === '/landing'}
|
||||||
<div in:blur={{ amount: 1 }} class="flex justify-center items-center h-screen">
|
<div in:blur={{ amount: 1 }} class="flex justify-center items-center h-screen">
|
||||||
<div class="flex flex-col space-y-1.5">
|
<div class="flex flex-col space-y-1.5">
|
||||||
<div>
|
<div>
|
||||||
|
@ -38,20 +38,20 @@
|
||||||
<div class="flex place-content-around mx-auto space-x-2">
|
<div class="flex place-content-around mx-auto space-x-2">
|
||||||
<ThemeSwitcher />
|
<ThemeSwitcher />
|
||||||
|
|
||||||
<Button click={() => ($state = 'login')}>
|
<Button click={() => ($state = '/login')}>
|
||||||
<LogIn />
|
<LogIn />
|
||||||
<p>Login</p>
|
<p>Login</p>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button click={() => ($state = 'register')}>
|
<Button click={() => ($state = '/register')}>
|
||||||
<UserPlus />
|
<UserPlus />
|
||||||
<p>Register</p>
|
<p>Register</p>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{:else if $state === 'login'}
|
{:else if $state === '/login'}
|
||||||
<LoginForm callback={() => ($state = 'landing')} />
|
<LoginForm callback={() => ($state = '/landing')} />
|
||||||
{:else if $state === 'register'}
|
{:else if $state === '/register'}
|
||||||
<RegisterForm callback={() => ($state = 'landing')} />
|
<RegisterForm callback={() => ($state = '/landing')} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { json } from '@sveltejs/kit';
|
||||||
|
|
||||||
|
/** @type {import('./$types').RequestHandler} */
|
||||||
|
export async function POST(event) {
|
||||||
|
const { request, cookies, locals } = event;
|
||||||
|
const body = await request.json();
|
||||||
|
}
|
Loading…
Reference in a new issue