Working on oauth

This commit is contained in:
2023-12-10 00:25:57 +01:00
parent ce84fa376b
commit 7fe980e478
13 changed files with 190 additions and 85 deletions

View File

@@ -11,7 +11,7 @@ datasource db {
model Maintenance {
id Int @id @default(autoincrement())
reason String
reason String @default("")
beginAt DateTime @default(now())
endAt DateTime @default(now())
createdAt DateTime @default(now())
@@ -21,24 +21,24 @@ model Maintenance {
model Announcement {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
content String
content String @default("")
}
model Category {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
slug String
name String
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
slug String @default("")
name String @default("")
talents CategoriesOnTalents[]
}
model Talent {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
logo String
name String @unique
website String
work String
logo String @default("")
name String @unique @default("")
website String @default("")
work String @default("")
favorite Boolean @default(false)
categories CategoriesOnTalents[]
}
@@ -56,24 +56,34 @@ model CategoriesOnTalents {
model Post {
id Int @id @default(autoincrement())
slug String @unique
slug String @unique @default("")
createdAt DateTime @default(now())
views Int @default(0)
likes Int @default(0)
}
model Suggestion {
id Int @id @default(autoincrement())
author String @unique
content String
added Boolean @default(false)
createdAt DateTime @default(now())
id Int @id @default(autoincrement())
email String @unique @default("")
content String @default("")
added Boolean @default(false)
createdAt DateTime @default(now())
}
model Form {
id Int @id @default(autoincrement())
name String
email String
content String
name String @default("")
email String @default("")
content String @default("")
createdAt DateTime @default(now())
}
model GuestbookMessage {
id Int @id @default(autoincrement())
message String @default("")
email String @unique @default("")
image String @default("")
username String @default("")
updatedAt DateTime @updatedAt
createdAt DateTime @default(now())
}

View File

@@ -36,9 +36,9 @@ const navs = [
icon: 'i-ph-shooting-star-bold',
},
{
label: 'Bookmarks',
to: '/bookmarks',
icon: 'i-ph-bookmarks-bold',
label: 'Guestbook',
to: '/guestbook',
icon: 'i-material-symbols-book-2-outline',
},
{
label: 'Contact',

View File

@@ -1,32 +0,0 @@
export async function usePost(slug: string) {
const {
data: post,
refresh: refreshPost,
} = useFetch('/api/article', {
method: 'POST',
body: {
slug,
},
})
const likes = ref(post.value?.likes)
const like = async () => {
const { data } = await useFetch('/api/like', { method: 'PUT' })
likes.value = data
}
const views = ref(post.value!.views)
const view = async () => {
const { data } = await useFetch('/api/view', { method: 'PUT' })
likes.value = data
}
return {
post,
like,
view,
refreshPost,
likes: computed(() => likes.value),
views: computed(() => views.value),
}
}

View File

@@ -4,6 +4,37 @@ import { providers } from '~~/types'
useHead({
title: 'Sign my guestbook • Arthur Danjou',
})
const { loggedIn, clear , user} = useUserSession()
const { data: messages, refresh } = useFetch('/api/messages', { method: 'get' })
const toast = useToast()
const messageContent = ref<string>('')
async function sign() {
if (messageContent.value.length < 7 || messageContent.value.length > 58)
return
await $fetch('/api/message', {
method: 'post',
body: {
message: messageContent.value,
},
}).then(() => {
toast.add({
title: `Thank's for leaving a message!`,
icon: 'i-material-symbols-check-circle-outline-rounded',
timeout: 4000,
})
}).catch(() => {
toast.add({
title: 'An error occured when signing the book!',
color: 'red',
})
})
messageContent.value = ''
await refresh()
}
</script>
<template>
@@ -20,10 +51,35 @@ useHead({
<div class="flex items-center gap-2 mb-4">
<UIcon name="i-ph-circle-wavy-question-bold" class="text-subtitle text-xl" />
<h1 class="text-lg font-bold">
Login to sign my book
Want to sign my book ?
</h1>
</div>
<div class="flex gap-2">
<div v-if="loggedIn" class="flex items-center justify-between gap-4">
<div class="w-full relative flex items-center">
<input
v-model="messageContent"
type="text"
required
min="7"
max="58"
class="w-full rounded-lg p-2 h-10 focus:outline-none bg-gray-100 dark:bg-stone-800"
placeholder="Leave a message"
>
<UButton
class="absolute right-1 top-1 text-gray-900 dark:text-white rounded-md"
label="Send"
:disabled="messageContent.trim().length < 7 || messageContent.trim().length > 58"
variant="soft"
@click.prevent="sign()"
/>
</div>
<UButton
@click.prevent="clear()"
>
Logout
</UButton>
</div>
<div v-else class="flex gap-2">
<UButton
v-for="provider in providers"
:key="provider.slug"
@@ -31,9 +87,37 @@ useHead({
:color="provider.color"
variant="solid"
:icon="provider.icon"
:to="provider.link"
external
/>
</div>
</div>
{{ user }}
<div v-if="messages" class="columns-1 md:columns-2 lg:columns-4 gap-8 space-y-8">
<div
v-for="message in messages"
:key="message.id"
class="overflow-hidden sm:p-6 px-4 py-5 border border-zinc-100 p-6 dark:border-zinc-700/40 rounded-lg"
>
<p class="text-sm text-subtitle">
{{ message.message }}
</p>
<div class="flex items-center gap-4 mt-4">
<div class="h-8 w-8 rounded-full">
<NuxtImg class="w-full h-full rounded-full" :src="message.image" alt="Nature" placeholder />
</div>
<p class="font-bold">
{{ message.username }}
</p>
</div>
</div>
</div>
<div v-else class="my-4 text-subtitle">
<div class="flex gap-2 items-center">
<UIcon name="i-eos-icons-loading" />
<p>The messages are loading...</p>
</div>
</div>
</section>
</template>

View File

@@ -1,4 +1,5 @@
<script lang="ts" setup>
import type { Category, Suggestion, Talent } from '@prisma/client'
import { useTalentsStore } from '~/store/talents'
import { providers } from '~~/types'
@@ -13,7 +14,7 @@ const { loggedIn, clear } = useUserSession()
const {
data: talents,
pending,
} = await useFetch('/api/talents', {
} = await useFetch<Array<Talent>>('/api/talents', {
method: 'get',
query: {
favorite: isFavorite,
@@ -28,9 +29,8 @@ function isCategory(category: string) {
const {
data: getCategories,
} = await useFetch('/api/categories', { method: 'GET' })
getCategories.value?.forEach((category: any) => categories.value.push({ label: category.name, slug: category.slug }))
} = await useFetch<Array<Category>>('/api/categories', { method: 'GET' })
getCategories.value!.forEach((category: any) => categories.value.push({ label: category.name, slug: category.slug }))
const appConfig = useAppConfig()
function getColor() {
@@ -43,7 +43,7 @@ async function suggest() {
if (suggestContent.value.trim().length < 4)
return
await $fetch('/api/suggestion', {
await $fetch<Suggestion>('/api/suggestion', {
method: 'post',
body: {
content: suggestContent.value,

View File

@@ -1,10 +1,30 @@
<script lang="ts" setup>
import type { Post as PrismaPost } from '@prisma/client'
import type { Post } from '~~/types'
const appConfig = useAppConfig()
const route = useRoute()
const { data: postContent } = await useAsyncData<Post>(`writing:${route.params.slug}`, () => queryContent<Post>(`/writing/${route.params.slug}`).findOne())
const {
data: post,
} = await useFetch<PrismaPost>('/api/article', {
method: 'post',
body: {
slug: route.params.slug.toString(),
},
})
const likes = ref(post.value?.likes)
async function like() {
const data = await $fetch<PrismaPost>('/api/like', {
method: 'PUT',
body: {
slug: post.value?.slug,
},
})
likes.value = data.likes
}
if (!postContent.value) {
throw showError({
@@ -13,10 +33,7 @@ if (!postContent.value) {
})
}
const { post, view, like, likes, views } = await usePost(route.params.slug.toString())
const format = (date: string) => useDateFormat(date, 'D MMMM YYYY').value.replaceAll('"', '')
onMounted(() => view())
useHead({
title: `${postContent.value?.title} • Arthur Danjou's shelf`,
})
@@ -38,10 +55,6 @@ const likeCookie = useCookie<boolean>(`post:like:${postContent.value.slug}`, {
maxAge: 604_800,
})
const isLiked = computed(() => {
return likeCookie.value === true
})
async function handleLike() {
await like()
likeCookie.value = true
@@ -72,7 +85,7 @@ async function handleLike() {
<span></span>
<div>{{ postContent.readingMins }} min</div>
<span></span>
<div>{{ views }} {{ views > 1 ? 'views' : 'view' }}</div>
<div>{{ post.views }} {{ post.views > 1 ? 'views' : 'view' }}</div>
</div>
</time>
<h1 class="text-4xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-5xl">
@@ -107,15 +120,7 @@ async function handleLike() {
</p>
<div class="flex gap-4 flex-wrap">
<UButton
v-if="isLiked"
:label="`${likes} ${likes > 1 ? 'likes' : 'like'}`"
icon="i-ph-heart-bold"
size="lg"
variant="solid"
/>
<UButton
v-else
:label="`${likes} ${likes > 1 ? 'likes' : 'like'}`"
:label="`${likes} ${likes! > 1 ? 'likes' : 'like'}`"
icon="i-ph-heart-bold"
size="lg"
variant="soft"

View File

@@ -8,7 +8,11 @@ export default defineEventHandler(async (event) => {
where: {
slug,
},
update: {},
update: {
views: {
increment: 1,
},
},
create: {
slug,
},

View File

@@ -0,0 +1,24 @@
import { z } from 'zod'
const MessageValidator = z.object({
message: z.string(),
}).parse
export default defineEventHandler(async (event) => {
const { message } = await readValidatedBody(event, MessageValidator)
const { user } = await requireUserSession(event)
return await usePrisma().guestbookMessage.upsert({
where: {
email: user.email,
},
update: {
message,
},
create: {
email: user.email,
image: user.picture,
username: user.username,
message,
},
})
})

View File

@@ -0,0 +1,7 @@
export default defineEventHandler(async () => {
return await usePrisma().guestbookMessage.findMany({
orderBy: {
updatedAt: 'desc',
},
})
})

View File

@@ -9,13 +9,13 @@ export default defineEventHandler(async (event) => {
const { user } = await requireUserSession(event)
return await usePrisma().suggestion.upsert({
where: {
author: user.email,
email: user.email,
},
update: {
content,
},
create: {
author: user.email,
email: user.email,
content,
},
})

View File

@@ -1,10 +1,13 @@
export default oauth.githubEventHandler({
export default oauth.googleEventHandler({
config: {
redirectUrl: '/talents',
},
async onSuccess(event: any, { user }: any) {
await setUserSession(event, {
user: {
email: user.email,
picture: user.photoUrl,
username: String(user.displayName).trim(),
picture: user.picture,
username: String(user.name).trim(),
},
})
return sendRedirect(event, '/')

View File

@@ -9,6 +9,7 @@ https://atinux.com/
https://yael.dev/
https://esm.dev/
https://antfu.me/
tom lienard
Categories:
BRAND

View File

@@ -1,4 +1,3 @@
import exp from 'node:constants'
import type { MarkdownParsedContent, ParsedContent } from '@nuxt/content/dist/runtime/types'
export enum ColorsTheme {