lots of changes
This commit is contained in:
parent
40d2342211
commit
3b1e627356
17
src/app.d.ts
vendored
17
src/app.d.ts
vendored
@ -3,11 +3,26 @@
|
||||
declare global {
|
||||
namespace App {
|
||||
// interface Error {}
|
||||
// interface Locals {}
|
||||
interface Locals {
|
||||
user: SessionUserInfo,
|
||||
project: SessionProjectInfo
|
||||
}
|
||||
// interface PageData {}
|
||||
// interface PageState {}
|
||||
// interface Platform {}
|
||||
}
|
||||
|
||||
interface SessionUserInfo {
|
||||
id: string,
|
||||
username: string,
|
||||
email: string,
|
||||
account_type: string
|
||||
}
|
||||
|
||||
interface SessionProjectInfo {
|
||||
id: string,
|
||||
project_name: string
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
|
@ -2,8 +2,9 @@ import { validateSession } from '$lib/services/auth.server';
|
||||
|
||||
/** @type {import('@sveltejs/kit').Handle} */
|
||||
export async function handle({ event, resolve }) {
|
||||
if(event.url.pathname === "/auth") return resolve(event);
|
||||
const session_id = event.cookies.get("token");
|
||||
if (session_id === undefined && event.url.pathname !== "/auth") return Response.redirect(event.url.host+"/auth", 303);
|
||||
if (session_id === undefined) return Response.redirect(event.url.host+"/auth", 303);
|
||||
|
||||
// @ts-ignore
|
||||
const user = await validateSession(session_id);
|
||||
|
69
src/lib/components/organisms/auth/login-form.svelte
Normal file
69
src/lib/components/organisms/auth/login-form.svelte
Normal file
@ -0,0 +1,69 @@
|
||||
<script lang="ts">
|
||||
import * as Form from "$lib/components/ui/form";
|
||||
import { Input } from "$lib/components/ui/input";
|
||||
import { Loader2 } from "lucide-svelte";
|
||||
import { loginSchema, type LoginSchema } from "./schema";
|
||||
import {
|
||||
type SuperValidated,
|
||||
type Infer,
|
||||
superForm,
|
||||
} from "sveltekit-superforms";
|
||||
import { zodClient } from "sveltekit-superforms/adapters";
|
||||
|
||||
let isLoading = false;
|
||||
|
||||
export let data: SuperValidated<Infer<LoginSchema>>;
|
||||
|
||||
const form = superForm(data, {
|
||||
validators: zodClient(loginSchema),
|
||||
onSubmit: () => {
|
||||
isLoading = true;
|
||||
},
|
||||
onUpdated: ({form: f}) => {
|
||||
isLoading = false;
|
||||
}
|
||||
});
|
||||
|
||||
const { form: formData, enhance, message } = form;
|
||||
</script>
|
||||
|
||||
|
||||
<form method="POST" action="?/login" use:enhance>
|
||||
{#if $message}
|
||||
<span class="block text-center text-red-600">{$message}</span>
|
||||
{/if}
|
||||
<Form.Field {form} name="email">
|
||||
<Form.Control let:attrs>
|
||||
<Form.Label>Email</Form.Label>
|
||||
<Input
|
||||
{...attrs}
|
||||
type="email"
|
||||
bind:value={$formData.email}
|
||||
disabled={isLoading}
|
||||
/>
|
||||
</Form.Control>
|
||||
<Form.FieldErrors />
|
||||
</Form.Field>
|
||||
|
||||
<Form.Field {form} name="password">
|
||||
<Form.Control let:attrs>
|
||||
<Form.Label>Password</Form.Label>
|
||||
<Input
|
||||
{...attrs}
|
||||
type="password"
|
||||
bind:value={$formData.password}
|
||||
disabled={isLoading}
|
||||
/>
|
||||
</Form.Control>
|
||||
<Form.FieldErrors />
|
||||
<Form.Description>A strong password with 6-20 characters.</Form.Description>
|
||||
</Form.Field>
|
||||
<Form.Button disabled={isLoading} class="w-full">
|
||||
{#if isLoading}
|
||||
<Loader2 class="mr-2 h-4 w-4 animate-spin" />
|
||||
{:else}
|
||||
Submit
|
||||
{/if}
|
||||
</Form.Button>
|
||||
</form>
|
||||
|
@ -1,9 +1,16 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const formSchema = z.object({
|
||||
export const signupSchema = z.object({
|
||||
name: z.string().min(2).max(16),
|
||||
email: z.string().min(2).max(50).email("Invalid email format"),
|
||||
password: z.string().min(6).max(20),
|
||||
});
|
||||
|
||||
export type FormSchema = typeof formSchema;
|
||||
export type SignupSchema = typeof signupSchema;
|
||||
|
||||
export const loginSchema = z.object({
|
||||
email: z.string().min(2).max(50).email("Invalid email format"),
|
||||
password: z.string().min(6).max(20),
|
||||
});
|
||||
|
||||
export type LoginSchema = typeof loginSchema;
|
@ -2,7 +2,7 @@
|
||||
import * as Form from "$lib/components/ui/form";
|
||||
import { Input } from "$lib/components/ui/input";
|
||||
import { Loader2 } from "lucide-svelte";
|
||||
import { formSchema, type FormSchema } from "./schema";
|
||||
import { signupSchema, type SignupSchema } from "./schema";
|
||||
import {
|
||||
type SuperValidated,
|
||||
type Infer,
|
||||
@ -12,10 +12,10 @@
|
||||
|
||||
let isLoading = false;
|
||||
|
||||
export let data: SuperValidated<Infer<FormSchema>>;
|
||||
export let data: SuperValidated<Infer<SignupSchema>>;
|
||||
|
||||
const form = superForm(data, {
|
||||
validators: zodClient(formSchema),
|
||||
validators: zodClient(signupSchema),
|
||||
onSubmit: () => {
|
||||
isLoading = true;
|
||||
},
|
||||
@ -66,8 +66,8 @@
|
||||
disabled={isLoading}
|
||||
/>
|
||||
</Form.Control>
|
||||
<Form.Description>A strong password with 6-20 characters.</Form.Description>
|
||||
<Form.FieldErrors />
|
||||
<Form.Description>A strong password with 6-20 characters.</Form.Description>
|
||||
</Form.Field>
|
||||
<Form.Button disabled={isLoading} class="w-full">
|
||||
{#if isLoading}
|
@ -1,3 +1,5 @@
|
||||
import bcrypt from "bcrypt";
|
||||
|
||||
export function generateId(length: number): string {
|
||||
var text = "";
|
||||
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
@ -5,3 +7,11 @@ export function generateId(length: number): string {
|
||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||
return text;
|
||||
}
|
||||
|
||||
export async function hashPassword(password: string): Promise<string> {
|
||||
return await bcrypt.hash(password, 10);
|
||||
}
|
||||
|
||||
export async function validatePassword(hashed_password: string, plain_password: string) {
|
||||
return await bcrypt.compare(plain_password, hashed_password);
|
||||
}
|
@ -4,7 +4,7 @@
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import Player from '$lib/components/organisms/player.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import { PlayIcon, Loader } from 'lucide-svelte';
|
||||
import { PlayIcon, Loader, Plus, MailIcon } from 'lucide-svelte';
|
||||
|
||||
let script = "";
|
||||
|
||||
@ -27,23 +27,26 @@
|
||||
<Player script={script} on:close={() => opened=false} />
|
||||
{/if}
|
||||
|
||||
<div>
|
||||
<Label>Your Latest Podcast</Label>
|
||||
<div class="flex gap-4 flex-col">
|
||||
<Label>Your Latest Podcasts</Label>
|
||||
{#each pods as pod}
|
||||
<Button class="flex w-full flex-row justify-start rounded text-left" variant="secondary" on:click={() => opened = true}>
|
||||
<div class="items-center justify-center rounded-lg p-2 pl-0">
|
||||
<PlayIcon class="h-4 w-4" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-md">Your Daily Pod</h3>
|
||||
<p class="text-sm text-muted-foreground">January 18th, 2024</p>
|
||||
<p class="text-sm text-muted-foreground">{new Date(pod.date_created).toLocaleDateString('de')}</p>
|
||||
</div>
|
||||
</Button>
|
||||
{/each}
|
||||
|
||||
<Label>Your Newsletters</Label>
|
||||
<div class="overflow-x-scroll scrollbars-hidden">
|
||||
{#each pods as pod, i}
|
||||
{#each pods as pod}
|
||||
<Button class="mt-4 flex w-full flex-row justify-start rounded text-left" variant="secondary">
|
||||
<div class="items-center justify-center rounded-lg p-2 pl-0">
|
||||
<PlayIcon class="h-4 w-4" />
|
||||
<MailIcon class="h-4 w-4" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-md">{pod.title}</h3>
|
||||
|
@ -1,31 +1,15 @@
|
||||
export async function GET() {
|
||||
import { db, podsTable } from "$lib/db"
|
||||
import { eq } from "drizzle-orm";
|
||||
|
||||
/**
|
||||
* @type {import("@sveltejs/kit").RequestHandler}
|
||||
*/
|
||||
export async function GET(event) {
|
||||
|
||||
const pods = await db.select().from(podsTable).where(eq(podsTable.user_id, event.locals.user.id)).orderBy(podsTable.date_created).limit(3);
|
||||
|
||||
return new Response(JSON.stringify({
|
||||
success: true,
|
||||
pods: [
|
||||
{
|
||||
title: "Your daily pod",
|
||||
date: new Date("2024-03-11")
|
||||
},
|
||||
{
|
||||
title: "Your rewind pod",
|
||||
date: new Date("2024-03-10")
|
||||
},
|
||||
{
|
||||
title: "Your pod today",
|
||||
date: new Date("2024-03-09")
|
||||
},
|
||||
{
|
||||
title: "My daily pod",
|
||||
date: new Date("2024-03-08")
|
||||
},
|
||||
{
|
||||
title: "I'm just making up stuff at this point",
|
||||
date: new Date("2024-03-07")
|
||||
},
|
||||
{
|
||||
title: "Tried that last one to see how long it can be",
|
||||
date: new Date("2024-03-06")
|
||||
}
|
||||
]
|
||||
}))
|
||||
pods
|
||||
}));
|
||||
}
|
@ -1,16 +1,19 @@
|
||||
import { setError, superValidate } from "sveltekit-superforms";
|
||||
import { setError, setMessage, superValidate } from "sveltekit-superforms";
|
||||
import { fail } from "@sveltejs/kit";
|
||||
import { formSchema } from "$lib/components/organisms/auth/schema";
|
||||
import { loginSchema, signupSchema } from "$lib/components/organisms/auth/schema";
|
||||
import { zod } from "sveltekit-superforms/adapters";
|
||||
import { db, usersTable } from "$lib/db";
|
||||
import bcrypt from "bcrypt";
|
||||
import * as authService from "$lib/services/auth.server";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { hashPassword, validatePassword } from "$lib/utils/auth.utils";
|
||||
/**
|
||||
* @type {import("./$types").PageServerLoad}
|
||||
*/
|
||||
export const load = async () => {
|
||||
return {
|
||||
form: await superValidate(zod(formSchema)),
|
||||
signupForm: await superValidate(zod(signupSchema)),
|
||||
loginForm: await superValidate(zod(loginSchema)),
|
||||
};
|
||||
};
|
||||
|
||||
@ -19,7 +22,7 @@ export const load = async () => {
|
||||
*/
|
||||
export const actions = {
|
||||
signup: async (event) => {
|
||||
const form = await superValidate(event, zod(formSchema));
|
||||
const form = await superValidate(event, zod(signupSchema));
|
||||
if (!form.valid) {
|
||||
return fail(400, {
|
||||
form,
|
||||
@ -35,7 +38,7 @@ export const actions = {
|
||||
const newUser = await db.insert(usersTable).values({
|
||||
name: form.data.name,
|
||||
email: form.data.email,
|
||||
hashed_password: (await bcrypt.hash(form.data.password, 10)),
|
||||
hashed_password: (await hashPassword(form.data.password)),
|
||||
}).returning({ id: usersTable.id }).onConflictDoNothing({ target: usersTable.email });
|
||||
|
||||
if (newUser.length === 0) return setError(form, "email", "Email already taken.", {
|
||||
@ -50,6 +53,42 @@ export const actions = {
|
||||
secure: false
|
||||
});
|
||||
|
||||
return {
|
||||
form,
|
||||
};
|
||||
},
|
||||
login: async (event) => {
|
||||
const form = await superValidate(event, zod(loginSchema));
|
||||
if (!form.valid) {
|
||||
return fail(400, {
|
||||
form,
|
||||
});
|
||||
}
|
||||
|
||||
// await (async () => {
|
||||
// return new Promise((res, rej) => {
|
||||
// setTimeout(res, 5000)
|
||||
// })
|
||||
// })()
|
||||
|
||||
const user = await db.select().from(usersTable).where(eq(usersTable.email, form.data.email));
|
||||
|
||||
if (user.length === 0) return setMessage(form, "Invalid login credentials", {
|
||||
status: 409
|
||||
});
|
||||
|
||||
if(!validatePassword(user[0].hashed_password, form.data.password)) return setMessage(form, "Invalid login credentials", {
|
||||
status: 409
|
||||
});
|
||||
|
||||
const sessionId = await authService.createSession(user[0].id);
|
||||
|
||||
event.cookies.set("token", sessionId, {
|
||||
path: "/",
|
||||
expires: new Date("01-01-2025"),
|
||||
secure: false
|
||||
});
|
||||
|
||||
return {
|
||||
form,
|
||||
};
|
||||
|
@ -1,8 +1,10 @@
|
||||
<script>
|
||||
import AuthForm from '$lib/components/organisms/auth/auth-form.svelte';
|
||||
import LoginForm from '$lib/components/organisms/auth/login-form.svelte';
|
||||
import AuthForm from '$lib/components/organisms/auth/signup-form.svelte';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
|
||||
let authIsOpen = false;
|
||||
let isLogin = false;
|
||||
|
||||
/** @type {import('./$types').PageData} */
|
||||
export let data;
|
||||
@ -16,12 +18,21 @@
|
||||
|
||||
{#if authIsOpen}
|
||||
<div class="pt-8 text-left">
|
||||
<AuthForm data={data.form} />
|
||||
{#if data.loginForm.message || data.signupForm.message}
|
||||
<p class="color-red">{data.loginForm.message || data.signupForm.message}</p>
|
||||
{/if}
|
||||
{#if isLogin}
|
||||
<LoginForm data={data.loginForm}/>
|
||||
<p class="text-center">Don't have an account? <a class="text-primary font-bold" on:click={() => isLogin = false} href="#">Sign up</a></p>
|
||||
{:else}
|
||||
<AuthForm data={data.signupForm} />
|
||||
<p class="text-center">Already have an account? <a class="text-primary font-bold" on:click={() => isLogin = true} href="#">Log in</a></p>
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="mt-[100%]">
|
||||
<Button class="w-full" on:click={() => (authIsOpen = true)}>Sign up</Button>
|
||||
<Button class="mt-4 w-full" variant="ghost">Log in</Button>
|
||||
<Button class="mt-4 w-full" variant="ghost" on:click={() => (authIsOpen = isLogin = true)}>Log in</Button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user