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 {
|
declare global {
|
||||||
namespace App {
|
namespace App {
|
||||||
// interface Error {}
|
// interface Error {}
|
||||||
// interface Locals {}
|
interface Locals {
|
||||||
|
user: SessionUserInfo,
|
||||||
|
project: SessionProjectInfo
|
||||||
|
}
|
||||||
// interface PageData {}
|
// interface PageData {}
|
||||||
// interface PageState {}
|
// interface PageState {}
|
||||||
// interface Platform {}
|
// interface Platform {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface SessionUserInfo {
|
||||||
|
id: string,
|
||||||
|
username: string,
|
||||||
|
email: string,
|
||||||
|
account_type: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SessionProjectInfo {
|
||||||
|
id: string,
|
||||||
|
project_name: string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export {};
|
export {};
|
||||||
|
@ -2,8 +2,9 @@ import { validateSession } from '$lib/services/auth.server';
|
|||||||
|
|
||||||
/** @type {import('@sveltejs/kit').Handle} */
|
/** @type {import('@sveltejs/kit').Handle} */
|
||||||
export async function handle({ event, resolve }) {
|
export async function handle({ event, resolve }) {
|
||||||
|
if(event.url.pathname === "/auth") return resolve(event);
|
||||||
const session_id = event.cookies.get("token");
|
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
|
// @ts-ignore
|
||||||
const user = await validateSession(session_id);
|
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";
|
import { z } from "zod";
|
||||||
|
|
||||||
export const formSchema = z.object({
|
export const signupSchema = z.object({
|
||||||
name: z.string().min(2).max(16),
|
name: z.string().min(2).max(16),
|
||||||
email: z.string().min(2).max(50).email("Invalid email format"),
|
email: z.string().min(2).max(50).email("Invalid email format"),
|
||||||
password: z.string().min(6).max(20),
|
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 * as Form from "$lib/components/ui/form";
|
||||||
import { Input } from "$lib/components/ui/input";
|
import { Input } from "$lib/components/ui/input";
|
||||||
import { Loader2 } from "lucide-svelte";
|
import { Loader2 } from "lucide-svelte";
|
||||||
import { formSchema, type FormSchema } from "./schema";
|
import { signupSchema, type SignupSchema } from "./schema";
|
||||||
import {
|
import {
|
||||||
type SuperValidated,
|
type SuperValidated,
|
||||||
type Infer,
|
type Infer,
|
||||||
@ -12,10 +12,10 @@
|
|||||||
|
|
||||||
let isLoading = false;
|
let isLoading = false;
|
||||||
|
|
||||||
export let data: SuperValidated<Infer<FormSchema>>;
|
export let data: SuperValidated<Infer<SignupSchema>>;
|
||||||
|
|
||||||
const form = superForm(data, {
|
const form = superForm(data, {
|
||||||
validators: zodClient(formSchema),
|
validators: zodClient(signupSchema),
|
||||||
onSubmit: () => {
|
onSubmit: () => {
|
||||||
isLoading = true;
|
isLoading = true;
|
||||||
},
|
},
|
||||||
@ -66,8 +66,8 @@
|
|||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
/>
|
/>
|
||||||
</Form.Control>
|
</Form.Control>
|
||||||
<Form.Description>A strong password with 6-20 characters.</Form.Description>
|
|
||||||
<Form.FieldErrors />
|
<Form.FieldErrors />
|
||||||
|
<Form.Description>A strong password with 6-20 characters.</Form.Description>
|
||||||
</Form.Field>
|
</Form.Field>
|
||||||
<Form.Button disabled={isLoading} class="w-full">
|
<Form.Button disabled={isLoading} class="w-full">
|
||||||
{#if isLoading}
|
{#if isLoading}
|
@ -1,3 +1,5 @@
|
|||||||
|
import bcrypt from "bcrypt";
|
||||||
|
|
||||||
export function generateId(length: number): string {
|
export function generateId(length: number): string {
|
||||||
var text = "";
|
var text = "";
|
||||||
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
@ -5,3 +7,11 @@ export function generateId(length: number): string {
|
|||||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||||
return text;
|
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 { Button } from '$lib/components/ui/button';
|
||||||
import Player from '$lib/components/organisms/player.svelte';
|
import Player from '$lib/components/organisms/player.svelte';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { PlayIcon, Loader } from 'lucide-svelte';
|
import { PlayIcon, Loader, Plus, MailIcon } from 'lucide-svelte';
|
||||||
|
|
||||||
let script = "";
|
let script = "";
|
||||||
|
|
||||||
@ -27,23 +27,26 @@
|
|||||||
<Player script={script} on:close={() => opened=false} />
|
<Player script={script} on:close={() => opened=false} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div>
|
<div class="flex gap-4 flex-col">
|
||||||
<Label>Your Latest Podcast</Label>
|
<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}>
|
<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">
|
<div class="items-center justify-center rounded-lg p-2 pl-0">
|
||||||
<PlayIcon class="h-4 w-4" />
|
<PlayIcon class="h-4 w-4" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3 class="text-md">Your Daily Pod</h3>
|
<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>
|
</div>
|
||||||
</Button>
|
</Button>
|
||||||
|
{/each}
|
||||||
|
|
||||||
<Label>Your Newsletters</Label>
|
<Label>Your Newsletters</Label>
|
||||||
<div class="overflow-x-scroll scrollbars-hidden">
|
<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">
|
<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">
|
<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>
|
||||||
<div>
|
<div>
|
||||||
<h3 class="text-md">{pod.title}</h3>
|
<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({
|
return new Response(JSON.stringify({
|
||||||
success: true,
|
success: true,
|
||||||
pods: [
|
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")
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}))
|
|
||||||
}
|
}
|
@ -1,16 +1,19 @@
|
|||||||
import { setError, superValidate } from "sveltekit-superforms";
|
import { setError, setMessage, superValidate } from "sveltekit-superforms";
|
||||||
import { fail } from "@sveltejs/kit";
|
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 { zod } from "sveltekit-superforms/adapters";
|
||||||
import { db, usersTable } from "$lib/db";
|
import { db, usersTable } from "$lib/db";
|
||||||
import bcrypt from "bcrypt";
|
import bcrypt from "bcrypt";
|
||||||
import * as authService from "$lib/services/auth.server";
|
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}
|
* @type {import("./$types").PageServerLoad}
|
||||||
*/
|
*/
|
||||||
export const load = async () => {
|
export const load = async () => {
|
||||||
return {
|
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 = {
|
export const actions = {
|
||||||
signup: async (event) => {
|
signup: async (event) => {
|
||||||
const form = await superValidate(event, zod(formSchema));
|
const form = await superValidate(event, zod(signupSchema));
|
||||||
if (!form.valid) {
|
if (!form.valid) {
|
||||||
return fail(400, {
|
return fail(400, {
|
||||||
form,
|
form,
|
||||||
@ -35,7 +38,7 @@ export const actions = {
|
|||||||
const newUser = await db.insert(usersTable).values({
|
const newUser = await db.insert(usersTable).values({
|
||||||
name: form.data.name,
|
name: form.data.name,
|
||||||
email: form.data.email,
|
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 });
|
}).returning({ id: usersTable.id }).onConflictDoNothing({ target: usersTable.email });
|
||||||
|
|
||||||
if (newUser.length === 0) return setError(form, "email", "Email already taken.", {
|
if (newUser.length === 0) return setError(form, "email", "Email already taken.", {
|
||||||
@ -50,6 +53,42 @@ export const actions = {
|
|||||||
secure: false
|
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 {
|
return {
|
||||||
form,
|
form,
|
||||||
};
|
};
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
<script>
|
<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';
|
import { Button } from '$lib/components/ui/button';
|
||||||
|
|
||||||
let authIsOpen = false;
|
let authIsOpen = false;
|
||||||
|
let isLogin = false;
|
||||||
|
|
||||||
/** @type {import('./$types').PageData} */
|
/** @type {import('./$types').PageData} */
|
||||||
export let data;
|
export let data;
|
||||||
@ -16,12 +18,21 @@
|
|||||||
|
|
||||||
{#if authIsOpen}
|
{#if authIsOpen}
|
||||||
<div class="pt-8 text-left">
|
<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>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="mt-[100%]">
|
<div class="mt-[100%]">
|
||||||
<Button class="w-full" on:click={() => (authIsOpen = true)}>Sign up</Button>
|
<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>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user