final-video
This commit is contained in:
parent
28b1cb60f1
commit
de136939fc
@ -35,7 +35,10 @@
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@lucia-auth/adapter-prisma": "^0.4.0",
|
||||
"@lucia-auth/sveltekit": "^0.6.2",
|
||||
"@picocss/pico": "^1.5.7",
|
||||
"@prisma/client": "^4.9.0"
|
||||
"@prisma/client": "^4.9.0",
|
||||
"lucia-auth": "^0.6.0"
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
lockfileVersion: 5.4
|
||||
|
||||
specifiers:
|
||||
'@lucia-auth/adapter-prisma': ^0.4.0
|
||||
'@lucia-auth/sveltekit': ^0.6.2
|
||||
'@picocss/pico': ^1.5.7
|
||||
'@playwright/test': ^1.30.0
|
||||
'@prisma/client': ^4.9.0
|
||||
@ -11,6 +13,7 @@ specifiers:
|
||||
eslint: ^8.33.0
|
||||
eslint-config-prettier: ^8.6.0
|
||||
eslint-plugin-svelte3: ^4.0.0
|
||||
lucia-auth: ^0.6.0
|
||||
prettier: ^2.8.3
|
||||
prettier-plugin-svelte: ^2.9.0
|
||||
prisma: ^4.9.0
|
||||
@ -23,8 +26,11 @@ specifiers:
|
||||
vitest: ^0.25.8
|
||||
|
||||
dependencies:
|
||||
'@lucia-auth/adapter-prisma': 0.4.0_lucia-auth@0.6.0
|
||||
'@lucia-auth/sveltekit': 0.6.2_iyasqsbplmktfcni36hpxhd3zi
|
||||
'@picocss/pico': 1.5.7
|
||||
'@prisma/client': 4.9.0_prisma@4.9.0
|
||||
lucia-auth: 0.6.0
|
||||
|
||||
devDependencies:
|
||||
'@playwright/test': 1.30.0
|
||||
@ -299,6 +305,29 @@ packages:
|
||||
'@jridgewell/sourcemap-codec': 1.4.14
|
||||
dev: true
|
||||
|
||||
/@lucia-auth/adapter-prisma/0.4.0_lucia-auth@0.6.0:
|
||||
resolution: {integrity: sha512-HMaGbVfB5KTZBs6KPqI5z6RFKVl4AKfm9u2KrrQglthpd/rWJoJRDI2oYkdcEX4Xu1FNdNJJrEtKKBtVAXYqrw==}
|
||||
peerDependencies:
|
||||
lucia-auth: 0.6.x
|
||||
dependencies:
|
||||
lucia-auth: 0.6.0
|
||||
dev: false
|
||||
|
||||
/@lucia-auth/sveltekit/0.6.2_iyasqsbplmktfcni36hpxhd3zi:
|
||||
resolution: {integrity: sha512-+lOhgctcdVkPRYtJegTaEZYLFhPJOHFjdAm9cBFFjpkV6cNGuHSTxobfOn7yRy3pGVhzGFM91uOfYrJxmECOBA==}
|
||||
peerDependencies:
|
||||
lucia-auth: 0.5.x - 0.6.x
|
||||
svelte: 3.x
|
||||
dependencies:
|
||||
'@noble/hashes': 1.2.0
|
||||
lucia-auth: 0.6.0
|
||||
svelte: 3.55.1
|
||||
dev: false
|
||||
|
||||
/@noble/hashes/1.2.0:
|
||||
resolution: {integrity: sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==}
|
||||
dev: false
|
||||
|
||||
/@nodelib/fs.scandir/2.1.5:
|
||||
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
||||
engines: {node: '>= 8'}
|
||||
@ -1321,6 +1350,13 @@ packages:
|
||||
yallist: 4.0.0
|
||||
dev: true
|
||||
|
||||
/lucia-auth/0.6.0:
|
||||
resolution: {integrity: sha512-8j5nPl3RbbqGoZWULER4q+2PP7i8F3Eq3OeN7EnL+bxi4YAn5I+5FcNq/ikUCj8neOxGZyzJiRzFJq+t3r28+g==}
|
||||
dependencies:
|
||||
'@noble/hashes': 1.2.0
|
||||
nanoid: 4.0.1
|
||||
dev: false
|
||||
|
||||
/magic-string/0.27.0:
|
||||
resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==}
|
||||
engines: {node: '>=12'}
|
||||
@ -1389,6 +1425,12 @@ packages:
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/nanoid/4.0.1:
|
||||
resolution: {integrity: sha512-udKGtCCUafD3nQtJg9wBhRP3KMbPglUsgV5JVsXhvyBs/oefqb4sqMEhKBBgqZncYowu58p1prsZQBYvAj/Gww==}
|
||||
engines: {node: ^14 || ^16 || >=18}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/natural-compare-lite/1.4.0:
|
||||
resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==}
|
||||
dev: true
|
||||
@ -1807,7 +1849,6 @@ packages:
|
||||
/svelte/3.55.1:
|
||||
resolution: {integrity: sha512-S+87/P0Ve67HxKkEV23iCdAh/SX1xiSfjF1HOglno/YTbSTW7RniICMCofWGdJJbdjw3S+0PfFb1JtGfTXE0oQ==}
|
||||
engines: {node: '>= 8'}
|
||||
dev: true
|
||||
|
||||
/text-table/0.2.0:
|
||||
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
|
||||
|
@ -14,4 +14,39 @@ model Article {
|
||||
id Int @id @default(autoincrement())
|
||||
title String
|
||||
content String
|
||||
User User @relation(fields: [userId], references: [id])
|
||||
userId String
|
||||
}
|
||||
|
||||
model User {
|
||||
id String @id @unique
|
||||
name String
|
||||
username String @unique
|
||||
articles Article[]
|
||||
session Session[]
|
||||
Key Key[]
|
||||
|
||||
@@map("user")
|
||||
}
|
||||
|
||||
model Session {
|
||||
id String @id @unique
|
||||
user_id String
|
||||
active_expires BigInt
|
||||
idle_expires BigInt
|
||||
user User @relation(references: [id], fields: [user_id], onDelete: Cascade)
|
||||
|
||||
@@index([user_id])
|
||||
@@map("session")
|
||||
}
|
||||
|
||||
model Key {
|
||||
id String @id @unique
|
||||
hashed_password String?
|
||||
user_id String
|
||||
primary Boolean
|
||||
user User @relation(references: [id], fields: [user_id], onDelete: Cascade)
|
||||
|
||||
@@index([user_id])
|
||||
@@map("key")
|
||||
}
|
||||
|
15
src/app.d.ts
vendored
15
src/app.d.ts
vendored
@ -3,11 +3,24 @@ import type { PrismaClient } from "@prisma/client"
|
||||
declare global {
|
||||
namespace App {
|
||||
// interface Error {}
|
||||
// interface Locals {}
|
||||
interface Locals {
|
||||
validate: import("@lucia-auth/sveltekit").Validate
|
||||
validateUser: import("@lucia-auth/sveltekit").ValidateUser
|
||||
setSession: import("@lucia-auth/sveltekit").SetSession
|
||||
}
|
||||
// interface PageData {}
|
||||
// interface Platform {}
|
||||
}
|
||||
var __prisma: PrismaClient
|
||||
|
||||
/// <reference types="lucia-auth" />
|
||||
declare namespace Lucia {
|
||||
type Auth = import("$lib/server/lucia").Auth
|
||||
type UserAttributes = {
|
||||
username: string
|
||||
name: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export {}
|
||||
|
10
src/hooks.server.ts
Normal file
10
src/hooks.server.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { handleHooks } from "@lucia-auth/sveltekit"
|
||||
import { auth } from "$lib/server/lucia"
|
||||
import type { Handle } from "@sveltejs/kit"
|
||||
import { sequence } from "@sveltejs/kit/hooks"
|
||||
|
||||
export const customHandle: Handle = async ({ resolve, event }) => {
|
||||
return resolve(event)
|
||||
}
|
||||
|
||||
export const handle: Handle = sequence(handleHooks(auth), customHandle)
|
18
src/lib/server/lucia.ts
Normal file
18
src/lib/server/lucia.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import lucia from "lucia-auth"
|
||||
import prismaAdapter from "@lucia-auth/adapter-prisma"
|
||||
import { dev } from "$app/environment"
|
||||
import { prisma } from "$lib/server/prisma"
|
||||
|
||||
export const auth = lucia({
|
||||
adapter: prismaAdapter(prisma),
|
||||
env: dev ? "DEV" : "PROD",
|
||||
transformUserData: (userData) => {
|
||||
return {
|
||||
userId: userData.id,
|
||||
username: userData.username,
|
||||
name: userData.name,
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
export type Auth = typeof auth
|
@ -1,3 +1,6 @@
|
||||
import type { LayoutServerLoad } from "./$types"
|
||||
|
||||
export const load: LayoutServerLoad = async () => {}
|
||||
export const load: LayoutServerLoad = async ({ locals }) => {
|
||||
const { user, session } = await locals.validateUser()
|
||||
return { user }
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
<script lang="ts">
|
||||
import '@picocss/pico'
|
||||
import type { PageData } from './$types'
|
||||
export let data: PageData
|
||||
</script>
|
||||
|
||||
<div class="container">
|
||||
@ -12,9 +14,17 @@
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><a href="/">Home</a></li>
|
||||
<li><a href="/register">Register</a></li>
|
||||
<li><a href="/login" role="button">Login</a></li>
|
||||
<form method="POST">
|
||||
<li><a href="/">Home</a></li>
|
||||
{#if !data.user}
|
||||
<li><a href="/register">Register</a></li>
|
||||
<li><a href="/login" role="button">Login</a></li>
|
||||
{:else}
|
||||
<li>
|
||||
<button formaction="/logout" type="submit" role="button">Logout</button>
|
||||
</li>
|
||||
{/if}
|
||||
</form>
|
||||
</ul>
|
||||
</nav>
|
||||
<slot />
|
||||
|
@ -1,6 +1,6 @@
|
||||
import type { Actions, PageServerLoad } from "./$types"
|
||||
import { prisma } from "$lib/server/prisma"
|
||||
import { fail } from "@sveltejs/kit"
|
||||
import { error, fail, redirect } from "@sveltejs/kit"
|
||||
|
||||
export const load: PageServerLoad = async () => {
|
||||
return {
|
||||
@ -9,7 +9,12 @@ export const load: PageServerLoad = async () => {
|
||||
}
|
||||
|
||||
export const actions: Actions = {
|
||||
createArticle: async ({ request }) => {
|
||||
createArticle: async ({ request, locals }) => {
|
||||
const { user, session } = await locals.validateUser()
|
||||
if (!(user && session)) {
|
||||
throw redirect(302, "/")
|
||||
}
|
||||
|
||||
const { title, content } = Object.fromEntries(
|
||||
await request.formData(),
|
||||
) as Record<string, string>
|
||||
@ -19,6 +24,7 @@ export const actions: Actions = {
|
||||
data: {
|
||||
title,
|
||||
content,
|
||||
userId: user.userId,
|
||||
},
|
||||
})
|
||||
} catch (err) {
|
||||
@ -30,13 +36,27 @@ export const actions: Actions = {
|
||||
status: 201,
|
||||
}
|
||||
},
|
||||
deleteArticle: async ({ url }) => {
|
||||
deleteArticle: async ({ url, locals }) => {
|
||||
const { user, session } = await locals.validateUser()
|
||||
if (!(user && session)) {
|
||||
throw redirect(302, "/")
|
||||
}
|
||||
const id = url.searchParams.get("id")
|
||||
if (!id) {
|
||||
return fail(400, { message: "Invalid request" })
|
||||
}
|
||||
|
||||
try {
|
||||
const article = await prisma.article.findUniqueOrThrow({
|
||||
where: {
|
||||
id: Number(id),
|
||||
},
|
||||
})
|
||||
|
||||
if (article.userId !== user.userId) {
|
||||
throw error(403, "Not authorized")
|
||||
}
|
||||
|
||||
await prisma.article.delete({
|
||||
where: {
|
||||
id: Number(id),
|
||||
|
@ -15,21 +15,25 @@
|
||||
<p>
|
||||
{article.content}
|
||||
</p>
|
||||
<form action="?/deleteArticle&id={article.id}" method="POST">
|
||||
<button type="submit" class="outline secondary">Delete Article</button>
|
||||
</form>
|
||||
<a href="/{article.id}" role="button" class="outline constrast" style="width: 100%;"
|
||||
>Edit Article</a
|
||||
>
|
||||
{#if article.userId === data.user?.userId}
|
||||
<form action="?/deleteArticle&id={article.id}" method="POST">
|
||||
<button type="submit" class="outline secondary">Delete Article</button>
|
||||
</form>
|
||||
<a href="/{article.id}" role="button" class="outline constrast" style="width: 100%;"
|
||||
>Edit Article</a
|
||||
>
|
||||
{/if}
|
||||
</article>
|
||||
{/each}
|
||||
</div>
|
||||
<form action="?/createArticle" method="POST">
|
||||
<h3>New Article</h3>
|
||||
<label for="title"> Title </label>
|
||||
<input type="text" id="title" name="title" />
|
||||
<label for="title"> Content </label>
|
||||
<textarea id="content" name="content" rows={5} />
|
||||
<button type="submit">Add Article</button>
|
||||
</form>
|
||||
{#if data.user}
|
||||
<form action="?/createArticle" method="POST">
|
||||
<h3>New Article</h3>
|
||||
<label for="title"> Title </label>
|
||||
<input type="text" id="title" name="title" />
|
||||
<label for="title"> Content </label>
|
||||
<textarea id="content" name="content" rows={5} />
|
||||
<button type="submit">Add Article</button>
|
||||
</form>
|
||||
{/if}
|
||||
</div>
|
||||
|
@ -2,8 +2,13 @@ import type { Actions, PageServerLoad } from "./$types"
|
||||
import { prisma } from "$lib/server/prisma"
|
||||
import { error, fail } from "@sveltejs/kit"
|
||||
|
||||
export const load: PageServerLoad = async ({ params }) => {
|
||||
const getArticle = async () => {
|
||||
export const load: PageServerLoad = async ({ params, locals }) => {
|
||||
const { user, session } = await locals.validateUser()
|
||||
if (!(user && session)) {
|
||||
throw error(401, "Unauthorized")
|
||||
}
|
||||
|
||||
const getArticle = async (userId: string) => {
|
||||
const article = await prisma.article.findUnique({
|
||||
where: {
|
||||
id: Number(params.articleId),
|
||||
@ -12,21 +17,39 @@ export const load: PageServerLoad = async ({ params }) => {
|
||||
if (!article) {
|
||||
throw error(404, "Article not found")
|
||||
}
|
||||
if (article.userId !== user.userId) {
|
||||
throw error(403, "Unauthorized")
|
||||
}
|
||||
|
||||
return article
|
||||
}
|
||||
|
||||
return {
|
||||
article: getArticle(),
|
||||
article: getArticle(user.userId),
|
||||
}
|
||||
}
|
||||
|
||||
export const actions: Actions = {
|
||||
updateArticle: async ({ request, params }) => {
|
||||
updateArticle: async ({ request, params, locals }) => {
|
||||
const { user, session } = await locals.validateUser()
|
||||
if (!(user && session)) {
|
||||
throw error(401, "Unauthorized")
|
||||
}
|
||||
|
||||
const { title, content } = Object.fromEntries(
|
||||
await request.formData(),
|
||||
) as Record<string, string>
|
||||
|
||||
try {
|
||||
const article = await prisma.article.findUniqueOrThrow({
|
||||
where: {
|
||||
id: Number(params.articleId),
|
||||
},
|
||||
})
|
||||
|
||||
if (article.userId !== user.userId) {
|
||||
throw error(403, "Forbidden to edit this article.")
|
||||
}
|
||||
await prisma.article.update({
|
||||
where: {
|
||||
id: Number(params.articleId),
|
||||
|
@ -0,0 +1,28 @@
|
||||
import { auth } from "$lib/server/lucia"
|
||||
import { fail, redirect } from "@sveltejs/kit"
|
||||
import type { Actions, PageServerLoad } from "./$types"
|
||||
|
||||
export const load: PageServerLoad = async ({ locals }) => {
|
||||
const session = await locals.validate()
|
||||
if (session) {
|
||||
throw redirect(302, "/")
|
||||
}
|
||||
}
|
||||
|
||||
export const actions: Actions = {
|
||||
default: async ({ request, locals }) => {
|
||||
const { username, password } = Object.fromEntries(
|
||||
await request.formData(),
|
||||
) as Record<string, string>
|
||||
|
||||
try {
|
||||
const key = await auth.validateKeyPassword("username", username, password)
|
||||
const session = await auth.createSession(key.userId)
|
||||
locals.setSession(session)
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
return fail(400, { message: "Could not login user." })
|
||||
}
|
||||
throw redirect(302, "/")
|
||||
},
|
||||
}
|
@ -1,5 +1,15 @@
|
||||
import { auth } from "$lib/server/lucia"
|
||||
import { redirect } from "@sveltejs/kit"
|
||||
import type { RequestHandler } from "./$types"
|
||||
|
||||
export const POST: RequestHandler = async () => {
|
||||
return new Response()
|
||||
export const POST: RequestHandler = async ({ locals }) => {
|
||||
const session = await locals.validate()
|
||||
if (!session) {
|
||||
throw redirect(302, "/")
|
||||
}
|
||||
|
||||
await auth.invalidateSession(session.sessionId)
|
||||
locals.setSession(null)
|
||||
|
||||
throw redirect(302, "/")
|
||||
}
|
||||
|
@ -0,0 +1,36 @@
|
||||
import { auth } from "$lib/server/lucia"
|
||||
import { fail, redirect } from "@sveltejs/kit"
|
||||
import type { Actions, PageServerLoad } from "./$types"
|
||||
|
||||
export const load: PageServerLoad = async ({ locals }) => {
|
||||
const session = await locals.validate()
|
||||
if (session) {
|
||||
throw redirect(302, "/")
|
||||
}
|
||||
}
|
||||
|
||||
export const actions: Actions = {
|
||||
default: async ({ request }) => {
|
||||
const { name, username, password } = Object.fromEntries(
|
||||
await request.formData(),
|
||||
) as Record<string, string>
|
||||
|
||||
try {
|
||||
await auth.createUser({
|
||||
key: {
|
||||
providerId: "username",
|
||||
providerUserId: username,
|
||||
password,
|
||||
},
|
||||
attributes: {
|
||||
name,
|
||||
username,
|
||||
},
|
||||
})
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
return fail(400, { message: "Could not register user" })
|
||||
}
|
||||
throw redirect(302, "/login")
|
||||
},
|
||||
}
|
Loading…
Reference in New Issue
Block a user