mirror of
https://github.com/ArthurDanjou/website.git
synced 2026-01-14 20:19:35 +01:00
Working on oauth
This commit is contained in:
@@ -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',
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -8,7 +8,11 @@ export default defineEventHandler(async (event) => {
|
||||
where: {
|
||||
slug,
|
||||
},
|
||||
update: {},
|
||||
update: {
|
||||
views: {
|
||||
increment: 1,
|
||||
},
|
||||
},
|
||||
create: {
|
||||
slug,
|
||||
},
|
||||
|
||||
24
src/server/api/message.post.ts
Normal file
24
src/server/api/message.post.ts
Normal 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,
|
||||
},
|
||||
})
|
||||
})
|
||||
7
src/server/api/messages.get.ts
Normal file
7
src/server/api/messages.get.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export default defineEventHandler(async () => {
|
||||
return await usePrisma().guestbookMessage.findMany({
|
||||
orderBy: {
|
||||
updatedAt: 'desc',
|
||||
},
|
||||
})
|
||||
})
|
||||
@@ -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,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -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, '/')
|
||||
|
||||
Reference in New Issue
Block a user