Lucia Authentication #8
|
|
@ -31,6 +31,7 @@
|
|||
"@sveltejs/adapter-auto": "^3.0.0",
|
||||
"@sveltejs/kit": "^2.9.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^5.0.0",
|
||||
"@types/bun": "^1.1.14",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"eslint": "^9.7.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
|
|
@ -41,6 +42,7 @@
|
|||
"prettier-plugin-tailwindcss": "^0.6.5",
|
||||
"prisma": "^6.0.1",
|
||||
"storybook": "^8.4.7",
|
||||
"storybook-dark-mode": "^4.0.2",
|
||||
"svelte": "^5.0.0",
|
||||
"svelte-check": "^4.0.0",
|
||||
"tailwindcss": "^3.4.9",
|
||||
|
|
@ -56,14 +58,14 @@
|
|||
"@prisma/client": "6.0.1",
|
||||
"@supabase/supabase-js": "^2.47.6",
|
||||
"@tailwindcss/typography": "^0.5.15",
|
||||
"@types/bun": "^1.1.14",
|
||||
"dayjs": "^1.11.13",
|
||||
"graphql": "^16.9.0",
|
||||
"graphql-yoga": "^5.10.4",
|
||||
"lucia": "^3.2.2",
|
||||
"oslo": "^1.2.1",
|
||||
"pino": "^9.5.0",
|
||||
"pino-pretty": "^13.0.0",
|
||||
"storybook-dark-mode": "^4.0.2",
|
||||
"tailwind-merge": "^2.5.5",
|
||||
"zod": "^3.24.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
BIN
prisma/dev.db
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
Warnings:
|
||||
|
||||
- Made the column `content` on table `Post` required. This step will fail if there are existing NULL values in that column.
|
||||
- Made the column `name` on table `User` required. This step will fail if there are existing NULL values in that column.
|
||||
|
||||
*/
|
||||
-- RedefineTables
|
||||
PRAGMA defer_foreign_keys=ON;
|
||||
PRAGMA foreign_keys=OFF;
|
||||
CREATE TABLE "new_Post" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"title" TEXT NOT NULL,
|
||||
"content" TEXT NOT NULL,
|
||||
"published" BOOLEAN DEFAULT false,
|
||||
"authorId" INTEGER NOT NULL,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT "Post_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||
);
|
||||
INSERT INTO "new_Post" ("authorId", "content", "id", "published", "title") SELECT "authorId", "content", "id", "published", "title" FROM "Post";
|
||||
DROP TABLE "Post";
|
||||
ALTER TABLE "new_Post" RENAME TO "Post";
|
||||
CREATE TABLE "new_User" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"email" TEXT,
|
||||
"name" TEXT NOT NULL,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
INSERT INTO "new_User" ("email", "id", "name") SELECT "email", "id", "name" FROM "User";
|
||||
DROP TABLE "User";
|
||||
ALTER TABLE "new_User" RENAME TO "User";
|
||||
CREATE UNIQUE INDEX "User_id_key" ON "User"("id");
|
||||
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
|
||||
PRAGMA foreign_keys=ON;
|
||||
PRAGMA defer_foreign_keys=OFF;
|
||||
|
|
@ -16,10 +16,13 @@ datasource db {
|
|||
|
||||
model User {
|
||||
id Int @id @default(autoincrement())
|
||||
email String @unique
|
||||
name String?
|
||||
email String? @unique
|
||||
name String
|
||||
posts Post[]
|
||||
sessions Session[]
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @default(now()) @updatedAt
|
||||
}
|
||||
|
||||
model Session {
|
||||
|
|
@ -35,10 +38,12 @@ model Session {
|
|||
|
||||
|
||||
model Post {
|
||||
id Int @id @default(autoincrement())
|
||||
id Int @id @default(autoincrement())
|
||||
title String
|
||||
content String?
|
||||
published Boolean @default(false)
|
||||
author User @relation(fields: [authorId], references: [id])
|
||||
content String
|
||||
published Boolean? @default(false)
|
||||
author User @relation(fields: [authorId], references: [id])
|
||||
authorId Int
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @default(now()) @updatedAt
|
||||
}
|
||||
16
src/app.css
|
|
@ -1,3 +1,19 @@
|
|||
@import 'tailwindcss/base';
|
||||
@import 'tailwindcss/components';
|
||||
@import 'tailwindcss/utilities';
|
||||
|
||||
:root {
|
||||
@apply text-slate-800;
|
||||
}
|
||||
|
||||
h1 {
|
||||
@apply font-display text-4xl;
|
||||
}
|
||||
|
||||
h2 {
|
||||
@apply font-display text-3xl;
|
||||
}
|
||||
|
||||
h3 {
|
||||
@apply font-display text-2xl;
|
||||
}
|
||||
|
|
@ -9,12 +9,12 @@
|
|||
backgroundColor,
|
||||
primary = false
|
||||
}: {
|
||||
type: HTMLButtonAttributes['type'];
|
||||
onClick: () => void;
|
||||
type?: HTMLButtonAttributes['type'];
|
||||
onClick?: () => void;
|
||||
label: string;
|
||||
size: 'small' | 'normal' | 'large';
|
||||
backgroundColor: string;
|
||||
primary: boolean;
|
||||
size?: 'small' | 'normal' | 'large';
|
||||
backgroundColor?: string;
|
||||
primary?: boolean;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
|
|
@ -31,7 +31,7 @@
|
|||
|
||||
<style>
|
||||
.button {
|
||||
@apply inline-block cursor-pointer rounded-full border-0 font-semibold leading-none transition-colors hover:opacity-80 hover:shadow-lg;
|
||||
@apply inline-block cursor-pointer rounded-lg border-0 font-semibold leading-none transition-colors hover:opacity-80 hover:shadow-lg;
|
||||
}
|
||||
.button--small {
|
||||
@apply px-2 py-0.5 text-sm;
|
||||
|
|
@ -40,7 +40,7 @@
|
|||
@apply px-2 py-1 text-base;
|
||||
}
|
||||
.button--large {
|
||||
@apply px-2.5 py-1 text-xl;
|
||||
@apply px-3 py-1.5 text-lg;
|
||||
}
|
||||
.button--primary {
|
||||
@apply bg-red-600 text-white;
|
||||
|
|
|
|||
43
src/lib/components/Input.stories.svelte
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<script module lang="ts">
|
||||
import { defineMeta } from '@storybook/addon-svelte-csf';
|
||||
import Input from './Input.svelte';
|
||||
|
||||
const { Story } = defineMeta({
|
||||
title: 'Input',
|
||||
component: Input,
|
||||
tags: ['autodocs'],
|
||||
argTypes: {
|
||||
type: {
|
||||
control: 'select',
|
||||
options: [
|
||||
'number',
|
||||
'button',
|
||||
'checkbox',
|
||||
'color',
|
||||
'date',
|
||||
'datetime-local',
|
||||
'email',
|
||||
'file',
|
||||
'hidden',
|
||||
'image',
|
||||
'month',
|
||||
'password',
|
||||
'radio',
|
||||
'range',
|
||||
'reset',
|
||||
'search',
|
||||
'submit',
|
||||
'tel',
|
||||
'text',
|
||||
'time',
|
||||
'url',
|
||||
'week'
|
||||
]
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<Story name="Text" args={{ label: 'Text', name: 'text', type: 'text' }} />
|
||||
<Story name="Password" args={{ label: 'Password', name: 'pass', type: 'password' }} />
|
||||
<Story name="Email" args={{ label: 'Email', name: 'email', type: 'email' }} />
|
||||
36
src/lib/components/Input.svelte
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<script lang="ts">
|
||||
import type { HTMLAttributes, HTMLInputTypeAttribute } from 'svelte/elements';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
type InputProps = {
|
||||
label?: string;
|
||||
name: string;
|
||||
type?: HTMLInputTypeAttribute;
|
||||
} & HTMLAttributes<HTMLDivElement>;
|
||||
|
||||
let { label, name, type = 'text', ...props }: InputProps = $props();
|
||||
</script>
|
||||
|
||||
<div {...props} class={twMerge('hestia-input', props.class)}>
|
||||
{#if label}
|
||||
<label for={name}>{label}</label>
|
||||
<div class="line"></div>
|
||||
{/if}
|
||||
<input {name} id={name} {type} />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.line {
|
||||
@apply h-8 border border-l-slate-200;
|
||||
}
|
||||
.hestia-input {
|
||||
@apply flex w-fit items-center gap-2 rounded-lg bg-slate-100 p-2 text-slate-700 outline-blue-400 focus-within:outline;
|
||||
}
|
||||
.hestia-input > label {
|
||||
@apply font-display text-lg;
|
||||
}
|
||||
.hestia-input > input {
|
||||
all: unset;
|
||||
@apply text-lg;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
<style>
|
||||
.navbar {
|
||||
@apply flex items-center justify-between border-slate-200 bg-slate-100 px-6 py-2 font-display drop-shadow;
|
||||
@apply flex items-center justify-between border-b border-slate-200 bg-slate-50 px-6 py-2 font-display drop-shadow;
|
||||
}
|
||||
|
||||
.navbar h1 {
|
||||
|
|
|
|||
|
|
@ -1,65 +0,0 @@
|
|||
import { prisma } from '$lib/prisma';
|
||||
import { Context } from '$lib/yoga/context';
|
||||
import SchemaBuilder from '@pothos/core';
|
||||
import PrismaPlugin, { type PrismaTypesFromClient } from '@pothos/plugin-prisma';
|
||||
|
||||
type ContextType = ReturnType<typeof Context>;
|
||||
|
||||
export const builder = new SchemaBuilder<{
|
||||
Context: ContextType;
|
||||
PrismaTypes: PrismaTypesFromClient<typeof prisma>;
|
||||
}>({
|
||||
plugins: [PrismaPlugin],
|
||||
prisma: {
|
||||
client: prisma,
|
||||
// defaults to false, uses /// comments from prisma schema as descriptions
|
||||
// for object types, relations and exposed fields.
|
||||
// descriptions can be omitted by setting description to false
|
||||
exposeDescriptions: false,
|
||||
// use where clause from prismaRelatedConnection for totalCount (defaults to true)
|
||||
filterConnectionTotalCount: true,
|
||||
// warn when not using a query parameter correctly
|
||||
onUnusedQuery: process.env.NODE_ENV === 'production' ? null : 'warn'
|
||||
}
|
||||
});
|
||||
|
||||
const User = builder.prismaObject('User', {
|
||||
fields: (t) => ({
|
||||
id: t.exposeID('id'),
|
||||
email: t.exposeString('email'),
|
||||
name: t.exposeString('name'),
|
||||
posts: t.relation('posts')
|
||||
})
|
||||
});
|
||||
|
||||
const Post = builder.prismaObject('Post', {
|
||||
fields: (t) => ({
|
||||
id: t.exposeID('id'),
|
||||
title: t.exposeString('title'),
|
||||
content: t.exposeString('content'),
|
||||
published: t.exposeBoolean('published'),
|
||||
author: t.relation('author')
|
||||
})
|
||||
});
|
||||
|
||||
builder.queryType({
|
||||
fields: (t) => ({
|
||||
version: t.string({
|
||||
resolve: (parent, args, context) => context.config.app_version
|
||||
}),
|
||||
users: t.prismaField({
|
||||
type: [User],
|
||||
resolve: async () => {
|
||||
return await prisma.user.findMany();
|
||||
}
|
||||
}),
|
||||
posts: t.prismaField({
|
||||
type: [Post],
|
||||
resolve: async () => {
|
||||
return await prisma.post.findMany();
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
export const Schema = builder.toSchema();
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { logger } from '$lib/logger';
|
||||
import { logger } from '$lib/server/logger';
|
||||
import { z } from 'zod';
|
||||
|
||||
export interface Configuration {
|
||||
28
src/lib/server/pothos/builder.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import { prisma } from '$lib/server/prisma';
|
||||
import type { Context } from '$lib/server/yoga';
|
||||
import SchemaBuilder from '@pothos/core';
|
||||
import PrismaPlugin, { type PrismaTypesFromClient } from '@pothos/plugin-prisma';
|
||||
import type { Scalars } from './schema/Scalars';
|
||||
|
||||
type PothosType = {
|
||||
Context: ReturnType<typeof Context>;
|
||||
PrismaTypes: PrismaTypesFromClient<typeof prisma>;
|
||||
Scalars: Scalars;
|
||||
};
|
||||
|
||||
SchemaBuilder.allowPluginReRegistration = true;
|
||||
|
||||
export const builder = new SchemaBuilder<PothosType>({
|
||||
plugins: [PrismaPlugin],
|
||||
prisma: {
|
||||
client: prisma,
|
||||
// defaults to false, uses /// comments from prisma schema as descriptions
|
||||
// for object types, relations and exposed fields.
|
||||
// descriptions can be omitted by setting description to false
|
||||
exposeDescriptions: false,
|
||||
// use where clause from prismaRelatedConnection for totalCount (defaults to true)
|
||||
filterConnectionTotalCount: true,
|
||||
// warn when not using a query parameter correctly
|
||||
onUnusedQuery: process.env.NODE_ENV === 'production' ? null : 'warn'
|
||||
}
|
||||
});
|
||||
1
src/lib/server/pothos/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export * from './schema';
|
||||
14
src/lib/server/pothos/schema/Scalars/Date.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import { builder } from '../../builder';
|
||||
|
||||
export const DateScalar = builder.scalarType('Date', {
|
||||
description: 'Date Scalar in ISO format',
|
||||
serialize: (date) => {
|
||||
return date.toISOString();
|
||||
},
|
||||
parseValue: (date) => {
|
||||
if (typeof date !== 'string') {
|
||||
throw new Error('Cyka blyat');
|
||||
}
|
||||
return new Date(date);
|
||||
}
|
||||
});
|
||||
8
src/lib/server/pothos/schema/Scalars/index.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
export * from './Date';
|
||||
|
||||
export type Scalars = {
|
||||
Date: {
|
||||
Input: Date;
|
||||
Output: Date;
|
||||
};
|
||||
};
|
||||
18
src/lib/server/pothos/schema/index.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import { builder } from '../builder';
|
||||
|
||||
builder.queryType({});
|
||||
|
||||
builder.queryField('version', (t) =>
|
||||
t.string({
|
||||
description: 'Application version',
|
||||
resolve: (parent, args, context) => context.config.app_version
|
||||
})
|
||||
);
|
||||
|
||||
builder.mutationType({});
|
||||
|
||||
import './Scalars';
|
||||
import './posts';
|
||||
import './users';
|
||||
|
||||
export const Schema = builder.toSchema();
|
||||
110
src/lib/server/pothos/schema/posts.ts
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
import { prisma } from '$lib/server/prisma';
|
||||
import { builder } from '../builder';
|
||||
|
||||
export const Post = builder.prismaObject('Post', {
|
||||
fields: (t) => ({
|
||||
id: t.exposeID('id'),
|
||||
title: t.exposeString('title'),
|
||||
content: t.exposeString('content'),
|
||||
published: t.exposeBoolean('published'),
|
||||
author: t.relation('author'),
|
||||
createdAt: t.expose('createdAt', {
|
||||
type: 'Date'
|
||||
}),
|
||||
updatedAt: t.expose('updatedAt', {
|
||||
type: 'Date'
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
const CreatePost = builder.inputType('CreatePost', {
|
||||
fields: (t) => ({
|
||||
title: t.string({
|
||||
required: true
|
||||
}),
|
||||
content: t.string({
|
||||
required: true
|
||||
}),
|
||||
published: t.boolean(),
|
||||
authorId: t.id({
|
||||
required: true
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
const UpdatePost = builder.inputType('UpdatePost', {
|
||||
fields: (t) => ({
|
||||
id: t.id({
|
||||
required: true
|
||||
}),
|
||||
title: t.string(),
|
||||
content: t.string(),
|
||||
published: t.boolean(),
|
||||
authorId: t.id()
|
||||
})
|
||||
});
|
||||
|
||||
builder.queryFields((t) => ({
|
||||
posts: t.prismaField({
|
||||
type: [Post],
|
||||
resolve: async () => {
|
||||
return await prisma.post.findMany();
|
||||
}
|
||||
})
|
||||
}));
|
||||
|
||||
builder.mutationFields((t) => ({
|
||||
createPost: t.field({
|
||||
type: Post,
|
||||
args: {
|
||||
input: t.arg({ required: true, type: CreatePost })
|
||||
},
|
||||
resolve: async (parent, args) => {
|
||||
const author = await prisma.user.findUnique({
|
||||
where: { id: Number(args.input.authorId) }
|
||||
});
|
||||
if (!author) {
|
||||
throw new Error('Author does not exist!');
|
||||
}
|
||||
const post = await prisma.post.create({
|
||||
data: {
|
||||
title: args.input.title,
|
||||
content: args.input.content,
|
||||
published: args.input.published,
|
||||
author: {
|
||||
connect: {
|
||||
id: author.id
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return post;
|
||||
}
|
||||
}),
|
||||
updatePost: t.field({
|
||||
type: Post,
|
||||
args: {
|
||||
input: t.arg({ required: true, type: UpdatePost })
|
||||
},
|
||||
resolve: async (parent, args) => {
|
||||
const post = await prisma.post.update({
|
||||
where: {
|
||||
id: Number(args.input.id)
|
||||
},
|
||||
data: {
|
||||
title: args.input.title ?? undefined,
|
||||
content: args.input.content ?? undefined,
|
||||
published: args.input.published,
|
||||
...(args.input.authorId && {
|
||||
author: {
|
||||
connect: {
|
||||
id: Number(args.input.authorId)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
return post;
|
||||
}
|
||||
})
|
||||
}));
|
||||
83
src/lib/server/pothos/schema/users.ts
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
import { prisma } from '$lib/server/prisma';
|
||||
import { builder } from '../builder';
|
||||
|
||||
export const User = builder.prismaObject('User', {
|
||||
fields: (t) => ({
|
||||
id: t.exposeID('id'),
|
||||
email: t.exposeString('email'),
|
||||
name: t.exposeString('name'),
|
||||
posts: t.relation('posts'),
|
||||
createdAt: t.expose('createdAt', {
|
||||
type: 'Date'
|
||||
}),
|
||||
updatedAt: t.expose('updatedAt', {
|
||||
type: 'Date'
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
const CreateUser = builder.inputType('CreateUser', {
|
||||
fields: (t) => ({
|
||||
email: t.string({
|
||||
required: true
|
||||
}),
|
||||
name: t.string({
|
||||
required: true
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
const UpdateUser = builder.inputType('UpdateUser', {
|
||||
fields: (t) => ({
|
||||
id: t.id({
|
||||
required: true
|
||||
}),
|
||||
email: t.string(),
|
||||
name: t.string()
|
||||
})
|
||||
});
|
||||
|
||||
builder.queryFields((t) => ({
|
||||
users: t.prismaField({
|
||||
type: [User],
|
||||
resolve: async () => {
|
||||
return await prisma.user.findMany();
|
||||
}
|
||||
})
|
||||
}));
|
||||
|
||||
builder.mutationFields((t) => ({
|
||||
createUser: t.field({
|
||||
type: User,
|
||||
args: {
|
||||
input: t.arg({ required: true, type: CreateUser })
|
||||
},
|
||||
resolve: async (parent, args) => {
|
||||
const post = await prisma.user.create({
|
||||
data: {
|
||||
email: args.input.email,
|
||||
name: args.input.name
|
||||
}
|
||||
});
|
||||
return post;
|
||||
}
|
||||
}),
|
||||
updateUser: t.field({
|
||||
type: User,
|
||||
args: {
|
||||
input: t.arg({ required: true, type: UpdateUser })
|
||||
},
|
||||
resolve: async (parent, args) => {
|
||||
const post = await prisma.user.update({
|
||||
where: {
|
||||
id: Number(args.input.id)
|
||||
},
|
||||
data: {
|
||||
email: args.input.email,
|
||||
name: args.input.name ?? undefined
|
||||
}
|
||||
});
|
||||
return post;
|
||||
}
|
||||
})
|
||||
}));
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { Config } from '$lib/config';
|
||||
import { Config } from '$lib/server/config';
|
||||
import type { YogaInitialContext } from 'graphql-yoga';
|
||||
|
||||
export const Context = (initialContext: YogaInitialContext) => ({
|
||||
2
src/lib/server/yoga/index.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
export * from './context';
|
||||
export * from './server';
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { yogaLogger } from '$lib/logger';
|
||||
import { Schema } from '$lib/pothos';
|
||||
import { yogaLogger } from '$lib/server/logger';
|
||||
import { Schema } from '$lib/server/pothos';
|
||||
import type { RequestEvent } from '@sveltejs/kit';
|
||||
import { createYoga } from 'graphql-yoga';
|
||||
import { Context } from './context';
|
||||
|
|
@ -3,4 +3,12 @@
|
|||
let { children } = $props();
|
||||
</script>
|
||||
|
||||
{@render children()}
|
||||
<div class="layout">
|
||||
{@render children()}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.layout {
|
||||
@apply h-screen w-screen animate-fade bg-slate-100;
|
||||
}
|
||||
</style>
|
||||
18
src/routes/+page.server.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
|
https://github.com/Machinata/hestia/pull/8/files#diff-b07092d78bfaa239d6ad0d01b0897c3ef49533b08512622d83c6f4e1dd3b363e
switched back to any type switched back to any type
Use the logger pls Use the logger pls
I dont think you can type event as a a Session, from the app typedefs you made it should be event.locals.[session or user idk] I dont think you can type event as a a Session, from the app typedefs you made it should be _event.locals.[session or user idk]_
https://github.com/Machinata/hestia/pull/8/files#diff-b07092d78bfaa239d6ad0d01b0897c3ef49533b08512622d83c6f4e1dd3b363e
switched back to any type switched back to any type
Use the logger pls Use the logger pls
|
||||
import { prisma } from '$lib/server/prisma';
|
||||
|
I dont think you can type event as a a Session, from the app typedefs you made it should be event.locals.[session or user idk] I dont think you can type event as a a Session, from the app typedefs you made it should be _event.locals.[session or user idk]_
https://github.com/Machinata/hestia/pull/8/files#diff-b07092d78bfaa239d6ad0d01b0897c3ef49533b08512622d83c6f4e1dd3b363e
switched back to any type switched back to any type
Use the logger pls Use the logger pls
|
||||
|
||||
|
I dont think you can type event as a a Session, from the app typedefs you made it should be event.locals.[session or user idk] I dont think you can type event as a a Session, from the app typedefs you made it should be _event.locals.[session or user idk]_
https://github.com/Machinata/hestia/pull/8/files#diff-b07092d78bfaa239d6ad0d01b0897c3ef49533b08512622d83c6f4e1dd3b363e
switched back to any type switched back to any type
Use the logger pls Use the logger pls
|
||||
export async function load(event) {
|
||||
|
I dont think you can type event as a a Session, from the app typedefs you made it should be event.locals.[session or user idk] I dont think you can type event as a a Session, from the app typedefs you made it should be _event.locals.[session or user idk]_
https://github.com/Machinata/hestia/pull/8/files#diff-b07092d78bfaa239d6ad0d01b0897c3ef49533b08512622d83c6f4e1dd3b363e
switched back to any type switched back to any type
Use the logger pls Use the logger pls
|
||||
const userId = event.cookies.get('user');
|
||||
|
I dont think you can type event as a a Session, from the app typedefs you made it should be event.locals.[session or user idk] I dont think you can type event as a a Session, from the app typedefs you made it should be _event.locals.[session or user idk]_
https://github.com/Machinata/hestia/pull/8/files#diff-b07092d78bfaa239d6ad0d01b0897c3ef49533b08512622d83c6f4e1dd3b363e
switched back to any type switched back to any type
Use the logger pls Use the logger pls
|
||||
if (!userId && isNaN(Number(userId))) {
|
||||
|
I dont think you can type event as a a Session, from the app typedefs you made it should be event.locals.[session or user idk] I dont think you can type event as a a Session, from the app typedefs you made it should be _event.locals.[session or user idk]_
https://github.com/Machinata/hestia/pull/8/files#diff-b07092d78bfaa239d6ad0d01b0897c3ef49533b08512622d83c6f4e1dd3b363e
switched back to any type switched back to any type
Use the logger pls Use the logger pls
|
||||
return {
|
||||
|
I dont think you can type event as a a Session, from the app typedefs you made it should be event.locals.[session or user idk] I dont think you can type event as a a Session, from the app typedefs you made it should be _event.locals.[session or user idk]_
https://github.com/Machinata/hestia/pull/8/files#diff-b07092d78bfaa239d6ad0d01b0897c3ef49533b08512622d83c6f4e1dd3b363e
switched back to any type switched back to any type
Use the logger pls Use the logger pls
|
||||
authenticated: false
|
||||
|
I dont think you can type event as a a Session, from the app typedefs you made it should be event.locals.[session or user idk] I dont think you can type event as a a Session, from the app typedefs you made it should be _event.locals.[session or user idk]_
https://github.com/Machinata/hestia/pull/8/files#diff-b07092d78bfaa239d6ad0d01b0897c3ef49533b08512622d83c6f4e1dd3b363e
switched back to any type switched back to any type
Use the logger pls Use the logger pls
|
||||
};
|
||||
|
I dont think you can type event as a a Session, from the app typedefs you made it should be event.locals.[session or user idk] I dont think you can type event as a a Session, from the app typedefs you made it should be _event.locals.[session or user idk]_
https://github.com/Machinata/hestia/pull/8/files#diff-b07092d78bfaa239d6ad0d01b0897c3ef49533b08512622d83c6f4e1dd3b363e
switched back to any type switched back to any type
Use the logger pls Use the logger pls
|
||||
}
|
||||
|
I dont think you can type event as a a Session, from the app typedefs you made it should be event.locals.[session or user idk] I dont think you can type event as a a Session, from the app typedefs you made it should be _event.locals.[session or user idk]_
https://github.com/Machinata/hestia/pull/8/files#diff-b07092d78bfaa239d6ad0d01b0897c3ef49533b08512622d83c6f4e1dd3b363e
switched back to any type switched back to any type
Use the logger pls Use the logger pls
|
||||
const user = await prisma.user.findUnique({
|
||||
|
I dont think you can type event as a a Session, from the app typedefs you made it should be event.locals.[session or user idk] I dont think you can type event as a a Session, from the app typedefs you made it should be _event.locals.[session or user idk]_
https://github.com/Machinata/hestia/pull/8/files#diff-b07092d78bfaa239d6ad0d01b0897c3ef49533b08512622d83c6f4e1dd3b363e
switched back to any type switched back to any type
Use the logger pls Use the logger pls
|
||||
where: {
|
||||
|
I dont think you can type event as a a Session, from the app typedefs you made it should be event.locals.[session or user idk] I dont think you can type event as a a Session, from the app typedefs you made it should be _event.locals.[session or user idk]_
https://github.com/Machinata/hestia/pull/8/files#diff-b07092d78bfaa239d6ad0d01b0897c3ef49533b08512622d83c6f4e1dd3b363e
switched back to any type switched back to any type
Use the logger pls Use the logger pls
|
||||
id: Number(userId)
|
||||
|
I dont think you can type event as a a Session, from the app typedefs you made it should be event.locals.[session or user idk] I dont think you can type event as a a Session, from the app typedefs you made it should be _event.locals.[session or user idk]_
https://github.com/Machinata/hestia/pull/8/files#diff-b07092d78bfaa239d6ad0d01b0897c3ef49533b08512622d83c6f4e1dd3b363e
switched back to any type switched back to any type
Use the logger pls Use the logger pls
|
||||
}
|
||||
|
I dont think you can type event as a a Session, from the app typedefs you made it should be event.locals.[session or user idk] I dont think you can type event as a a Session, from the app typedefs you made it should be _event.locals.[session or user idk]_
https://github.com/Machinata/hestia/pull/8/files#diff-b07092d78bfaa239d6ad0d01b0897c3ef49533b08512622d83c6f4e1dd3b363e
switched back to any type switched back to any type
Use the logger pls Use the logger pls
|
||||
});
|
||||
|
I dont think you can type event as a a Session, from the app typedefs you made it should be event.locals.[session or user idk] I dont think you can type event as a a Session, from the app typedefs you made it should be _event.locals.[session or user idk]_
https://github.com/Machinata/hestia/pull/8/files#diff-b07092d78bfaa239d6ad0d01b0897c3ef49533b08512622d83c6f4e1dd3b363e
switched back to any type switched back to any type
Use the logger pls Use the logger pls
|
||||
return {
|
||||
|
I dont think you can type event as a a Session, from the app typedefs you made it should be event.locals.[session or user idk] I dont think you can type event as a a Session, from the app typedefs you made it should be _event.locals.[session or user idk]_
https://github.com/Machinata/hestia/pull/8/files#diff-b07092d78bfaa239d6ad0d01b0897c3ef49533b08512622d83c6f4e1dd3b363e
switched back to any type switched back to any type
Use the logger pls Use the logger pls
|
||||
authenticated: !!user
|
||||
|
I dont think you can type event as a a Session, from the app typedefs you made it should be event.locals.[session or user idk] I dont think you can type event as a a Session, from the app typedefs you made it should be _event.locals.[session or user idk]_
https://github.com/Machinata/hestia/pull/8/files#diff-b07092d78bfaa239d6ad0d01b0897c3ef49533b08512622d83c6f4e1dd3b363e
switched back to any type switched back to any type
Use the logger pls Use the logger pls
|
||||
};
|
||||
|
I dont think you can type event as a a Session, from the app typedefs you made it should be event.locals.[session or user idk] I dont think you can type event as a a Session, from the app typedefs you made it should be _event.locals.[session or user idk]_
https://github.com/Machinata/hestia/pull/8/files#diff-b07092d78bfaa239d6ad0d01b0897c3ef49533b08512622d83c6f4e1dd3b363e
switched back to any type switched back to any type
Use the logger pls Use the logger pls
|
||||
}
|
||||
|
I dont think you can type event as a a Session, from the app typedefs you made it should be event.locals.[session or user idk] I dont think you can type event as a a Session, from the app typedefs you made it should be _event.locals.[session or user idk]_
https://github.com/Machinata/hestia/pull/8/files#diff-b07092d78bfaa239d6ad0d01b0897c3ef49533b08512622d83c6f4e1dd3b363e
switched back to any type switched back to any type
Use the logger pls Use the logger pls
|
||||
|
|
@ -2,10 +2,10 @@
|
|||
import { goto } from '$app/navigation';
|
||||
import Loader from '$lib/components/Loader.svelte';
|
||||
|
||||
let { data } = $props();
|
||||
|
||||
$effect(() => {
|
||||
const id = setTimeout(() => {
|
||||
goto('/app');
|
||||
}, 1500);
|
||||
const id = setTimeout(() => (data.authenticated ? goto('/app') : goto('/login')), 1500);
|
||||
return () => {
|
||||
clearTimeout(id);
|
||||
};
|
||||
|
|
@ -19,9 +19,6 @@
|
|||
|
||||
<style>
|
||||
.site-loader {
|
||||
@apply flex h-screen w-screen flex-col items-center justify-center gap-6 bg-slate-100;
|
||||
}
|
||||
.site-loader h1 {
|
||||
@apply font-display text-4xl;
|
||||
@apply flex h-screen w-screen flex-col items-center justify-center gap-6;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
import { Yoga } from '$lib/yoga';
|
||||
import { Yoga } from '$lib/server/yoga';
|
||||
|
||||
export { Yoga as GET, Yoga as POST };
|
||||
46
src/routes/login/+page.server.ts
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
import { logger } from '$lib/server/logger';
|
||||
import { prisma } from '$lib/server/prisma';
|
||||
import { error, redirect, type Actions } from '@sveltejs/kit';
|
||||
|
||||
export const actions = {
|
||||
login: async (event) => {
|
||||
const form = await event.request.formData();
|
||||
if (!form.has('email')) {
|
||||
return error(400, 'Email is a required form field!');
|
||||
}
|
||||
const user = await prisma.user.findUnique({
|
||||
where: {
|
||||
email: form.get('email') as string
|
||||
}
|
||||
});
|
||||
if (!user) {
|
||||
logger.error('User not found! ${user}');
|
||||
return error(401);
|
||||
|
rename to rename to `password` ty
load the password into a load the password into a `const` first like you did with login and please dont force types with `as`
Did for mem/storage space; added new const to conform Did for mem/storage space; added new const to conform
|
||||
}
|
||||
event.cookies.set('user', String(user.id), {
|
||||
path: '/',
|
||||
maxAge: 120
|
||||
});
|
||||
redirect(302, '/');
|
||||
},
|
||||
register: async (event) => {
|
||||
const form = await event.request.formData();
|
||||
if (!form.has('email') || !form.has('name')) {
|
||||
return error(400);
|
||||
}
|
||||
const user = await prisma.user.create({
|
||||
data: {
|
||||
email: form.get('email') as string,
|
||||
name: form.get('name') as string
|
||||
}
|
||||
});
|
||||
if (!user) {
|
||||
return error(500);
|
||||
}
|
||||
event.cookies.set('user', String(user.id), {
|
||||
path: '/',
|
||||
maxAge: 120
|
||||
});
|
||||
redirect(302, '/');
|
||||
}
|
||||
} satisfies Actions;
|
||||
49
src/routes/login/+page.svelte
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
<script lang="ts">
|
||||
import Button from '$lib/components/Button.svelte';
|
||||
import Input from '$lib/components/Input.svelte';
|
||||
import { fade, scale } from 'svelte/transition';
|
||||
|
||||
let mode: 'register' | 'login' = $state('login');
|
||||
let action = $derived(mode === 'login' ? '?/login' : '?/register');
|
||||
|
||||
function onViewToggle() {
|
||||
mode = mode === 'login' ? 'register' : 'login';
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="page">
|
||||
<h1 class="underline">Hestia</h1>
|
||||
<div class="login">
|
||||
<form method="POST" {action} transition:scale>
|
||||
<h2 transition:fade>{mode === 'login' ? 'Login' : 'Register'}</h2>
|
||||
{#if mode === 'register'}
|
||||
<div transition:fade>
|
||||
<Input label="Name" name="name" />
|
||||
</div>
|
||||
{/if}
|
||||
<Input label="Email" name="email" type="email" />
|
||||
<Input label="Password" name="password" type="password" />
|
||||
<div class="flex gap-2">
|
||||
<Button
|
||||
onClick={onViewToggle}
|
||||
label={mode === 'login' ? 'Register' : 'Login'}
|
||||
size="large"
|
||||
primary
|
||||
/>
|
||||
<Button type="submit" label="Submit" size="large" />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.page {
|
||||
@apply flex flex-col items-center justify-around gap-24 py-[10%];
|
||||
}
|
||||
.login {
|
||||
@apply w-fit max-w-lg animate-fade rounded-lg bg-white p-8;
|
||||
}
|
||||
.login > form {
|
||||
@apply flex w-full flex-col items-center gap-8 rounded-lg;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -8,6 +8,15 @@ export default {
|
|||
extend: {
|
||||
fontFamily: {
|
||||
display: ['Baskervville SC']
|
||||
},
|
||||
animation: {
|
||||
fade: 'fadeIn .5s ease-in-out'
|
||||
},
|
||||
keyframes: {
|
||||
fadeIn: {
|
||||
from: { opacity: '0' },
|
||||
to: { opacity: '1' }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
I dont think you can type event as a a Session, from the app typedefs you made it should be event.locals.[session or user idk]