lots of changes

This commit is contained in:
Omer Sabic 2024-03-12 20:14:29 +01:00
parent 40d2342211
commit 3b1e627356
10 changed files with 197 additions and 58 deletions

17
src/app.d.ts vendored
View File

@ -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 {};

View File

@ -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);

View 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>

View File

@ -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;

View File

@ -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}

View File

@ -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);
}

View File

@ -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>
<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>
</div>
</Button>
<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">{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>

View File

@ -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
}));
}

View File

@ -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,
};

View File

@ -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>