lint code

Signed-off-by: Arthur DANJOU <arthurdanjou@outlook.fr>
This commit is contained in:
2024-04-20 00:18:43 +02:00
parent c6ba8c791b
commit c698bfec8a
44 changed files with 180 additions and 177 deletions

View File

@@ -2,8 +2,8 @@
defineProps({ defineProps({
title: { title: {
type: String, type: String,
default: 'Uses Section title' default: 'Uses Section title',
} },
}) })
const appConfig = useAppConfig() const appConfig = useAppConfig()

View File

@@ -2,8 +2,8 @@
defineProps({ defineProps({
title: { title: {
type: String, type: String,
default: 'Uses Slot title' default: 'Uses Slot title',
} },
}) })
</script> </script>

View File

@@ -2,13 +2,13 @@
defineProps({ defineProps({
href: { href: {
type: String, type: String,
default: '' default: '',
}, },
target: { target: {
type: String, type: String,
default: undefined, default: undefined,
required: false required: false,
} },
}) })
const appConfig = useAppConfig() const appConfig = useAppConfig()

View File

@@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { useColorStore } from '~/store/color' import {useColorStore} from '~/store/color'
import { ColorsTheme } from '~~/types' import {ColorsTheme} from '~~/types'
const colors = Object.values(ColorsTheme) const colors = Object.values(ColorsTheme)
@@ -19,7 +19,7 @@ watch(isDark, () => {
:ui="{ :ui="{
background: 'bg-white dark:bg-stone-900', background: 'bg-white dark:bg-stone-900',
ring: 'ring-1 ring-gray-200 dark:ring-stone-800', ring: 'ring-1 ring-gray-200 dark:ring-stone-800',
container: 'z-30' container: 'z-30',
}" }"
> >
<template #default="{ open }"> <template #default="{ open }">
@@ -55,9 +55,9 @@ watch(isDark, () => {
color: { color: {
white: { white: {
solid: 'ring-0 bg-gray-100 dark:bg-gray-800 hover:bg-gray-100 dark:hover:bg-gray-800', solid: 'ring-0 bg-gray-100 dark:bg-gray-800 hover:bg-gray-100 dark:hover:bg-gray-800',
ghost: 'hover:bg-gray-50 dark:hover:bg-gray-800/50' ghost: 'hover:bg-gray-50 dark:hover:bg-gray-800/50',
} },
} },
}" }"
:variant="color === getColor ? 'solid' : 'ghost'" :variant="color === getColor ? 'solid' : 'ghost'"
@click.stop.prevent="setColor(color)" @click.stop.prevent="setColor(color)"

View File

@@ -10,7 +10,7 @@ function formatDate(date: number) {
const CardUi = { const CardUi = {
footer: { padding: 'px-4 py-2' }, footer: { padding: 'px-4 py-2' },
body: {base: 'h-full flex items-center'} body: { base: 'h-full flex items-center' },
} }
useIntervalFn(async () => await refresh(), 5000) useIntervalFn(async () => await refresh(), 5000)

View File

@@ -3,23 +3,23 @@ const socials = [
{ {
name: 'mail', name: 'mail',
icon: 'i-material-symbols-alternate-email', icon: 'i-material-symbols-alternate-email',
link: 'mailto:arthurdanjou@outlook.fr' link: 'mailto:arthurdanjou@outlook.fr',
}, },
{ {
name: 'twitter', name: 'twitter',
icon: 'i-ph-twitter-logo-bold', icon: 'i-ph-twitter-logo-bold',
link: 'https://twitter.com/ArthurDanj' link: 'https://twitter.com/ArthurDanj',
}, },
{ {
name: 'github', name: 'github',
icon: 'i-ph-github-logo-bold', icon: 'i-ph-github-logo-bold',
link: 'https://github.com/ArthurDanjou' link: 'https://github.com/ArthurDanjou',
}, },
{ {
name: 'linkedin', name: 'linkedin',
icon: 'i-ph-linkedin-logo-bold', icon: 'i-ph-linkedin-logo-bold',
link: 'https://www.linkedin.com/in/arthurdanjou/' link: 'https://www.linkedin.com/in/arthurdanjou/',
} },
] ]
</script> </script>

View File

@@ -1,11 +1,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { Stats } from '~~/types' import type {Stats} from '~~/types'
const stats = await $fetch<Stats>('/api/stats') const stats = await $fetch<Stats>('/api/stats')
const CardUi = { const CardUi = {
footer: { padding: 'px-4 py-2' }, footer: { padding: 'px-4 py-2' },
body: {base: 'h-full'} body: { base: 'h-full' },
} }
</script> </script>

View File

@@ -3,8 +3,8 @@ defineProps({
startDate: String, startDate: String,
endDate: { endDate: {
type: String, type: String,
required: true required: true,
} },
}) })
function formatTodayDate(date: string) { function formatTodayDate(date: string) {

View File

@@ -1,8 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Education } from '~~/types' import type {Education} from '~~/types'
defineProps({ defineProps({
education: Object as PropType<Education> education: Object as PropType<Education>,
}) })
</script> </script>

View File

@@ -1,8 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import type { WorkExperience } from '~~/types' import type {WorkExperience} from '~~/types'
defineProps({ defineProps({
experience: Object as PropType<WorkExperience> experience: Object as PropType<WorkExperience>,
}) })
</script> </script>

View File

@@ -1,8 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Skill } from '~~/types' import type {Skill} from '~~/types'
defineProps({ defineProps({
skill: Object as PropType<Skill> skill: Object as PropType<Skill>,
}) })
const { $colorMode } = useNuxtApp() const { $colorMode } = useNuxtApp()

View File

@@ -1,12 +1,12 @@
import { defineConfig } from 'drizzle-kit' import {defineConfig} from 'drizzle-kit'
export default defineConfig({ export default defineConfig({
driver: 'pg', driver: 'pg',
schema: './server/database/schema.ts', schema: './server/database/schema.ts',
out: './server/database/migrations', out: './server/database/migrations',
dbCredentials: { dbCredentials: {
connectionString: process.env.DATABASE_URL as string connectionString: process.env.DATABASE_URL as string,
}, },
strict: true, strict: true,
verbose: true verbose: true,
}) })

View File

@@ -1,3 +1,7 @@
import antfu from '@antfu/eslint-config' import antfu from '@antfu/eslint-config'
export default antfu() export default antfu({
rules: {
'node/prefer-global/process': 'off',
},
})

View File

@@ -11,14 +11,14 @@ export default defineNuxtRouteMiddleware(async (to) => {
if (isMaintenance.value && to.path !== '/maintenance') { if (isMaintenance.value && to.path !== '/maintenance') {
return navigateTo('/maintenance', { return navigateTo('/maintenance', {
redirectCode: 301 redirectCode: 301,
}) })
} }
if (!isMaintenance.value && to.path === '/maintenance') { if (!isMaintenance.value && to.path === '/maintenance') {
return navigateTo('/', { return navigateTo('/', {
redirectCode: 301, redirectCode: 301,
replace: true replace: true,
}) })
} }
}) })

View File

@@ -1,4 +1,3 @@
/* eslint-disable node/prefer-global/process */
export default defineNuxtRouteMiddleware((to) => { export default defineNuxtRouteMiddleware((to) => {
if (to.path === '/writing' && process.env.NODE_ENV !== 'development') { if (to.path === '/writing' && process.env.NODE_ENV !== 'development') {
return navigateTo('/', { return navigateTo('/', {

View File

@@ -1,10 +1,10 @@
import { defineNuxtModule } from 'nuxt/kit' import {defineNuxtModule} from 'nuxt/kit'
import { addCustomTab } from '@nuxt/devtools-kit' import {addCustomTab} from '@nuxt/devtools-kit'
export default defineNuxtModule({ export default defineNuxtModule({
meta: { meta: {
name: 'drizzle-studio', name: 'drizzle-studio',
version: '0.0.1' version: '0.0.1',
}, },
setup() { setup() {
addCustomTab({ addCustomTab({
@@ -13,8 +13,8 @@ export default defineNuxtModule({
icon: 'simple-icons:drizzle', icon: 'simple-icons:drizzle',
view: { view: {
type: 'iframe', type: 'iframe',
src: 'https://local.drizzle.studio/?themeId=azX2nOTScT9U6SWEmlq7z' src: 'https://local.drizzle.studio/?themeId=azX2nOTScT9U6SWEmlq7z',
} },
}) })
} },
}) })

View File

@@ -1,4 +1,3 @@
/* eslint-disable node/prefer-global/process */
export default defineNuxtConfig({ export default defineNuxtConfig({
css: [ css: [
'@/assets/css/main.scss', '@/assets/css/main.scss',

View File

@@ -3,12 +3,13 @@
"type": "module", "type": "module",
"private": true, "private": true,
"scripts": { "scripts": {
"db:studio": "prisma studio --browser none", "db:studio": "drizzle-kit studio",
"db:generate": "drizzle-kit generate:pg",
"build": "nuxt build", "build": "nuxt build",
"dev": "nuxt dev --host", "dev": "nuxt dev --host",
"generate": "nuxt generate", "generate": "nuxt generate",
"preview": "nuxt preview", "preview": "nuxt preview",
"postinstall": "nuxt prepare", "postinstall": "nuxt prepare && drizzle-kit generate:pg",
"lint": "eslint .", "lint": "eslint .",
"lint:fix": "eslint . --fix" "lint:fix": "eslint . --fix"
}, },
@@ -22,6 +23,7 @@
"drizzle-orm": "0.30.8", "drizzle-orm": "0.30.8",
"nuxt": "3.10.3", "nuxt": "3.10.3",
"nuxt-auth-utils": "0.0.20", "nuxt-auth-utils": "0.0.20",
"pg": "^8.11.5",
"pinia": "2.1.7", "pinia": "2.1.7",
"postcss-custom-properties": "13.3.7", "postcss-custom-properties": "13.3.7",
"postgres": "3.4.4", "postgres": "3.4.4",

View File

@@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
useHead({ useHead({
title: 'About me • Arthur Danjou' title: 'About me • Arthur Danjou',
}) })
const { data: skills } = await getSkills() const { data: skills } = await getSkills()

View File

@@ -1,8 +1,8 @@
<script lang="ts" setup> <script lang="ts" setup>
import { useBookmarksStore } from '~/store/bookmarks' import {useBookmarksStore} from '~/store/bookmarks'
useHead({ useHead({
title: 'Discover my library • Arthur Danjou' title: 'Discover my library • Arthur Danjou',
}) })
const categories = ref<Array<{ label: string, slug: string }>>([{ label: 'All', slug: 'all' }]) const categories = ref<Array<{ label: string, slug: string }>>([{ label: 'All', slug: 'all' }])
@@ -12,13 +12,13 @@ const { data: bookmarks, pending } = await useFetch('/api/bookmarks', {
method: 'get', method: 'get',
query: { query: {
favorite: isFavorite, favorite: isFavorite,
category: getCategory category: getCategory,
}, },
watch: [isFavorite, getCategory] watch: [isFavorite, getCategory],
}) })
const { data: getCategories } = await useFetch('/api/categories', { method: 'GET', query: { type: 'bookmark' } }) const { data: getCategories } = await useFetch('/api/categories', { method: 'GET', query: { type: 'bookmark' } })
getCategories.value!.forEach(category => categories.value.push({label: category.name, slug: category.slug})) getCategories.value!.forEach(category => categories.value.push({ label: category.name, slug: category.slug }))
function isCategory(slug: string) { function isCategory(slug: string) {
return getCategory.value === slug return getCategory.value === slug
@@ -30,7 +30,7 @@ const getMarkerStyle = computed(() => {
top: `${selected?.offsetTop}px`, top: `${selected?.offsetTop}px`,
left: `${selected?.offsetLeft === 12 ? 4 : selected?.offsetLeft}px`, left: `${selected?.offsetLeft === 12 ? 4 : selected?.offsetLeft}px`,
height: `${selected?.offsetHeight}px`, height: `${selected?.offsetHeight}px`,
width: `${selected?.offsetWidth}px` width: `${selected?.offsetWidth}px`,
} }
}) })

View File

@@ -1,8 +1,8 @@
<script lang="ts" setup> <script lang="ts" setup>
import { providers } from '~~/types' import {providers} from '~~/types'
useHead({ useHead({
title: 'Sign my guestbook • Arthur Danjou' title: 'Sign my guestbook • Arthur Danjou',
}) })
const { loggedIn, clear, user } = useUserSession() const { loggedIn, clear, user } = useUserSession()
@@ -20,20 +20,20 @@ async function sign() {
await $fetch('/api/message', { await $fetch('/api/message', {
method: 'post', method: 'post',
body: { body: {
message: messageContent.value message: messageContent.value,
} },
}).then(async () => { }).then(async () => {
toast.add({ toast.add({
title: `Thanks for leaving a message!`, title: `Thanks for leaving a message!`,
description: 'Your can see it at the top of the messages.', description: 'Your can see it at the top of the messages.',
icon: 'i-material-symbols-check-circle-outline-rounded', icon: 'i-material-symbols-check-circle-outline-rounded',
timeout: 4000 timeout: 4000,
}) })
await refresh() await refresh()
}).catch(() => { }).catch(() => {
toast.add({ toast.add({
title: 'An error occurred when signing the book!', title: 'An error occurred when signing the book!',
color: 'red' color: 'red',
}) })
}) })
messageContent.value = '' messageContent.value = ''
@@ -45,20 +45,20 @@ async function deleteMessage(id: number) {
await $fetch('/api/message', { await $fetch('/api/message', {
method: 'delete', method: 'delete',
body: { body: {
id id,
} },
}).then(async () => { }).then(async () => {
toast.add({ toast.add({
title: `Message successfully deleted`, title: `Message successfully deleted`,
icon: 'i-material-symbols-check-circle-outline-rounded', icon: 'i-material-symbols-check-circle-outline-rounded',
color: 'green', color: 'green',
timeout: 4000 timeout: 4000,
}) })
await refresh() await refresh()
}).catch(() => { }).catch(() => {
toast.add({ toast.add({
title: 'An error occured when deleting a message!', title: 'An error occured when deleting a message!',
color: 'red' color: 'red',
}) })
}) })
} }

View File

@@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
useHead({ useHead({
title: 'Arthur Danjou • Software Engineer and Maths Lover' title: 'Arthur Danjou • Software Engineer and Maths Lover',
}) })
</script> </script>

View File

@@ -1,10 +1,10 @@
<script lang="ts" setup> <script lang="ts" setup>
definePageMeta({ definePageMeta({
layout: 'maintenance' layout: 'maintenance',
}) })
useHead({ useHead({
title: 'Site under maintenance • Arthur Danjou' title: 'Site under maintenance • Arthur Danjou',
}) })
const { data: maintenance } = await useFetch('/api/maintenance') const { data: maintenance } = await useFetch('/api/maintenance')
@@ -17,23 +17,23 @@ const socials = [
{ {
name: 'mail', name: 'mail',
icon: 'i-material-symbols-alternate-email', icon: 'i-material-symbols-alternate-email',
link: 'mailto:arthurdanjou@outlook.fr' link: 'mailto:arthurdanjou@outlook.fr',
}, },
{ {
name: 'twitter', name: 'twitter',
icon: 'i-ph-twitter-logo-bold', icon: 'i-ph-twitter-logo-bold',
link: 'https://twitter.com/ArthurDanj' link: 'https://twitter.com/ArthurDanj',
}, },
{ {
name: 'github', name: 'github',
icon: 'i-ph-github-logo-bold', icon: 'i-ph-github-logo-bold',
link: 'https://github.com/ArthurDanjou' link: 'https://github.com/ArthurDanjou',
}, },
{ {
name: 'linkedin', name: 'linkedin',
icon: 'i-ph-linkedin-logo-bold', icon: 'i-ph-linkedin-logo-bold',
link: 'https://www.linkedin.com/in/arthurdanjou/' link: 'https://www.linkedin.com/in/arthurdanjou/',
} },
] ]
</script> </script>

View File

@@ -1,9 +1,9 @@
<script lang="ts" setup> <script lang="ts" setup>
import { useTalentsStore } from '~/store/talents' import {useTalentsStore} from '~/store/talents'
import { providers } from '~~/types' import {providers} from '~~/types'
useHead({ useHead({
title: 'Discover new talents • Arthur Danjou' title: 'Discover new talents • Arthur Danjou',
}) })
const categories = ref<Array<{ label: string, slug: string, id: number }>>([{ label: 'All', slug: 'all', id: 0 }]) const categories = ref<Array<{ label: string, slug: string, id: number }>>([{ label: 'All', slug: 'all', id: 0 }])
@@ -14,16 +14,16 @@ const { data: talents, pending } = await useFetch('/api/talents', {
method: 'get', method: 'get',
query: { query: {
favorite: isFavorite, favorite: isFavorite,
category: getCategory category: getCategory,
}, },
watch: [isFavorite, getCategory] watch: [isFavorite, getCategory],
}) })
const { data: getCategories } = await useFetch('/api/categories', { method: 'GET', query: { type: 'talent' } }) const { data: getCategories } = await useFetch('/api/categories', { method: 'GET', query: { type: 'talent' } })
getCategories.value!.forEach(category => categories.value.push({ getCategories.value!.forEach(category => categories.value.push({
label: category.name, label: category.name,
slug: category.slug, slug: category.slug,
id: category.id id: category.id,
})) }))
function isCategory(slug: string) { function isCategory(slug: string) {
@@ -36,7 +36,7 @@ const getMarkerStyle = computed(() => {
top: `${selected?.offsetTop}px`, top: `${selected?.offsetTop}px`,
left: `${selected?.offsetLeft === 12 ? 4 : selected?.offsetLeft}px`, left: `${selected?.offsetLeft === 12 ? 4 : selected?.offsetLeft}px`,
height: `${selected?.offsetHeight}px`, height: `${selected?.offsetHeight}px`,
width: `${selected?.offsetWidth}px` width: `${selected?.offsetWidth}px`,
} }
}) })
@@ -56,19 +56,19 @@ async function suggest() {
await $fetch('/api/suggestion', { await $fetch('/api/suggestion', {
method: 'post', method: 'post',
body: { body: {
content: suggestContent.value content: suggestContent.value,
} },
}).then((response) => { }).then((response) => {
toast.add({ toast.add({
title: `Your suggestion for '${response[0].content}' has been successfully added`, title: `Your suggestion for '${response[0].content}' has been successfully added`,
color: 'green', color: 'green',
icon: 'i-material-symbols-check-circle-outline-rounded', icon: 'i-material-symbols-check-circle-outline-rounded',
timeout: 4000 timeout: 4000,
}) })
}).catch(() => { }).catch(() => {
toast.add({ toast.add({
title: 'An error occurred when suggesting someone', title: 'An error occurred when suggesting someone',
color: 'red' color: 'red',
}) })
}) })
suggestContent.value = '' suggestContent.value = ''

View File

@@ -1,6 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
useHead({ useHead({
title: 'My work • Arthur Danjou' title: 'My work • Arthur Danjou',
}) })
const { data: projects } = await getProjects() const { data: projects } = await getProjects()
</script> </script>

View File

@@ -1,18 +1,18 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { Post as PrismaPost } from '@prisma/client' import type {Post as PrismaPost} from '@prisma/client'
import type { Post } from '~~/types' import type {Post} from '~~/types'
const appConfig = useAppConfig() const appConfig = useAppConfig()
const route = useRoute() const route = useRoute()
const { data: postContent } = await useAsyncData<Post>(`writing:${route.params.slug}`, () => queryContent<Post>(`/writing/${route.params.slug}`).findOne()) const { data: postContent } = await useAsyncData<Post>(`writing:${route.params.slug}`, () => queryContent<Post>(`/writing/${route.params.slug}`).findOne())
const { const {
data: post data: post,
} = await useFetch<PrismaPost>('/api/article', { } = await useFetch<PrismaPost>('/api/article', {
method: 'post', method: 'post',
body: { body: {
slug: route.params.slug.toString() slug: route.params.slug.toString(),
} },
}) })
const likes = ref(post.value?.likes) const likes = ref(post.value?.likes)
@@ -20,8 +20,8 @@ async function like() {
const data = await $fetch<PrismaPost>('/api/like', { const data = await $fetch<PrismaPost>('/api/like', {
method: 'PUT', method: 'PUT',
body: { body: {
slug: post.value?.slug slug: post.value?.slug,
} },
}) })
likes.value = data.likes likes.value = data.likes
} }
@@ -29,30 +29,30 @@ async function like() {
if (!postContent.value) { if (!postContent.value) {
throw showError({ throw showError({
statusMessage: 'The post you are looking for was not found.', statusMessage: 'The post you are looking for was not found.',
statusCode: 404 statusCode: 404,
}) })
} }
const format = (date: string) => useDateFormat(date, 'D MMMM YYYY').value.replaceAll('"', '') const format = (date: string) => useDateFormat(date, 'D MMMM YYYY').value.replaceAll('"', '')
useHead({ useHead({
title: `${postContent.value?.title} • Arthur Danjou's shelf` title: `${postContent.value?.title} • Arthur Danjou's shelf`,
}) })
function top() { function top() {
window.scrollTo({ window.scrollTo({
top: 0, top: 0,
left: 0, left: 0,
behavior: 'smooth' behavior: 'smooth',
}) })
} }
const { copy, copied } = useClipboard({ const { copy, copied } = useClipboard({
source: `https://arthurdanjou.fr/writing/${route.params.slug}`, source: `https://arthurdanjou.fr/writing/${route.params.slug}`,
copiedDuring: 4000 copiedDuring: 4000,
}) })
const likeCookie = useCookie<boolean>(`post:like:${postContent.value.slug}`, { const likeCookie = useCookie<boolean>(`post:like:${postContent.value.slug}`, {
maxAge: 604_800 maxAge: 604_800,
}) })
async function handleLike() { async function handleLike() {

View File

@@ -3,7 +3,7 @@ const appConfig = useAppConfig()
const getColor = computed(() => `text-${appConfig.ui.primary}-500`) const getColor = computed(() => `text-${appConfig.ui.primary}-500`)
useHead({ useHead({
title: 'My Shelf • Arthur Danjou' title: 'My Shelf • Arthur Danjou',
}) })
const { data: posts } = await getPosts() const { data: posts } = await getPosts()

View File

@@ -1,5 +1,5 @@
export default defineEventHandler(async () => { export default defineEventHandler(async () => {
return useDB().query.announcements.findFirst({ return useDB().query.announcements.findFirst({
orderBy: (announcement, {asc}) => [asc(announcement.createdAt)] orderBy: (announcement, { asc }) => [asc(announcement.createdAt)],
}) })
}) })

View File

@@ -5,12 +5,12 @@ const PostSchema = z.object({ slug: z.string() }).parse
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
const { slug } = await readValidatedBody(event, PostSchema) const { slug } = await readValidatedBody(event, PostSchema)
return useDB().insert(tables.posts).values({ return useDB().insert(tables.posts).values({
slug slug,
}).onConflictDoUpdate({ }).onConflictDoUpdate({
target: tables.posts.id, target: tables.posts.id,
set: { set: {
views: sql`${tables.posts.views} views: sql`${tables.posts.views}
+ 1` + 1`,
} },
}) })
}) })

View File

@@ -7,14 +7,14 @@ export default defineEventHandler(async (event) => {
with: { with: {
bookmarkCategories: { bookmarkCategories: {
with: { with: {
category: true category: true,
} },
} },
} },
}) })
return bookmarks.filter(bookmark => return bookmarks.filter(bookmark =>
(category === 'all' || bookmark.bookmarkCategories.some(cat => cat.category.slug === category)) (category === 'all' || bookmark.bookmarkCategories.some(cat => cat.category.slug === category))
&& (favorite === 'false' || bookmark.favorite) && (favorite === 'false' || bookmark.favorite),
) )
}) })

View File

@@ -1,4 +1,4 @@
import { z } from 'zod' import {z} from 'zod'
const PostSchema = z.object({ slug: z.string() }).parse const PostSchema = z.object({ slug: z.string() }).parse
@@ -7,7 +7,7 @@ export default defineEventHandler(async (event) => {
return useDB().update(tables.posts) return useDB().update(tables.posts)
.set({ .set({
likes: sql`${tables.posts.likes} likes: sql`${tables.posts.likes}
+ 1` + 1`,
}) })
.where(eq(tables.posts.slug, slug)) .where(eq(tables.posts.slug, slug))
}) })

View File

@@ -1,9 +1,9 @@
export default defineEventHandler(async () => { export default defineEventHandler(async () => {
const maintenance = await useDB().query.maintenances.findFirst({ const maintenance = await useDB().query.maintenances.findFirst({
orderBy: [asc(tables.maintenances.createdAt)] orderBy: [asc(tables.maintenances.createdAt)],
}) })
let enabled = true let enabled = true
// eslint-disable-next-line node/prefer-global/process
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === 'development') {
enabled = false enabled = false
} }
@@ -17,6 +17,6 @@ export default defineEventHandler(async () => {
return { return {
enabled, enabled,
maintenance maintenance,
} }
}) })

View File

@@ -1,7 +1,7 @@
import { z } from 'zod' import {z} from 'zod'
const MessageValidator = z.object({ const MessageValidator = z.object({
id: z.number() id: z.number(),
}).parse }).parse
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {

View File

@@ -1,7 +1,7 @@
import { z } from 'zod' import {z} from 'zod'
const MessageValidator = z.object({ const MessageValidator = z.object({
message: z.string() message: z.string(),
}).parse }).parse
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
@@ -12,19 +12,19 @@ export default defineEventHandler(async (event) => {
await sendDiscordWebhookMessage(config, { await sendDiscordWebhookMessage(config, {
title: 'New guestbook message ✨', title: 'New guestbook message ✨',
description: `**${user.username}** has signed the book : "*${message}*"`, description: `**${user.username}** has signed the book : "*${message}*"`,
color: 15893567 color: 15893567,
}) })
return useDB().insert(tables.guestbookMessages) return useDB().insert(tables.guestbookMessages)
.values({ .values({
message, message,
email: user.email, email: user.email,
username: user.username, username: user.username,
image: user.picture image: user.picture,
}) })
.onConflictDoUpdate({ .onConflictDoUpdate({
target: tables.guestbookMessages.email, target: tables.guestbookMessages.email,
set: { set: {
message message,
} },
}) })
}) })

View File

@@ -8,8 +8,8 @@ export default defineCachedEventHandler(async (event) => {
coding, coding,
editors, editors,
os, os,
languages languages,
} }
}, { }, {
maxAge: 60 * 60 * 3 // 3 hours, maxAge: 60 * 60 * 3, // 3 hours,
}) })

View File

@@ -1,7 +1,7 @@
import { z } from 'zod' import {z} from 'zod'
const SuggestionValidator = z.object({ const SuggestionValidator = z.object({
content: z.string() content: z.string(),
}).parse }).parse
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
@@ -12,23 +12,23 @@ export default defineEventHandler(async (event) => {
await sendDiscordWebhookMessage(config, { await sendDiscordWebhookMessage(config, {
title: 'New suggestion ✨', title: 'New suggestion ✨',
description: `**${user.username}** has requested **${content}** for the talents page.`, description: `**${user.username}** has requested **${content}** for the talents page.`,
color: 15237114 color: 15237114,
}) })
return useDB().insert(tables.suggestions) return useDB().insert(tables.suggestions)
.values({ .values({
email: user.email, email: user.email,
content content,
}) })
.onConflictDoUpdate({ .onConflictDoUpdate({
target: tables.suggestions.email, target: tables.suggestions.email,
set: { set: {
content content,
}, },
setWhere: sql`${tables.suggestions.email} setWhere: sql`${tables.suggestions.email}
= =
${user.email}` ${user.email}`,
}).returning({ }).returning({
content: tables.suggestions.content content: tables.suggestions.content,
}) })
}) })

View File

@@ -7,14 +7,14 @@ export default defineEventHandler(async (event) => {
with: { with: {
talentCategories: { talentCategories: {
with: { with: {
category: true category: true,
} },
} },
} },
}) })
return talents.filter(talent => return talents.filter(talent =>
(category === 'all' || talent.talentCategories.some(cat => cat.category.slug === category)) (category === 'all' || talent.talentCategories.some(cat => cat.category.slug === category))
&& (favorite === 'false' || talent.favorite) && (favorite === 'false' || talent.favorite),
) )
}) })

View File

@@ -1,4 +1,4 @@
import { z } from 'zod' import {z} from 'zod'
const PostSchema = z.object({ slug: z.string() }).parse const PostSchema = z.object({ slug: z.string() }).parse
@@ -7,7 +7,7 @@ export default defineEventHandler(async (event) => {
return useDB().update(tables.posts) return useDB().update(tables.posts)
.set({ .set({
views: sql`${tables.posts.views} views: sql`${tables.posts.views}
+ 1` + 1`,
}) })
.where(eq(tables.posts.slug, slug)) .where(eq(tables.posts.slug, slug))
}) })

View File

@@ -1,5 +1,5 @@
import { boolean, date, integer, pgEnum, pgTable, primaryKey, serial, text, timestamp } from 'drizzle-orm/pg-core' import {boolean, date, integer, pgEnum, pgTable, primaryKey, serial, text, timestamp} from 'drizzle-orm/pg-core'
import { relations } from 'drizzle-orm' import {relations} from 'drizzle-orm'
// O B J E C T S // O B J E C T S
@@ -9,13 +9,13 @@ export const maintenances = pgTable('maintenances', {
enabled: boolean('enabled').default(false).notNull(), enabled: boolean('enabled').default(false).notNull(),
beginAt: date('begin_at').defaultNow().notNull(), beginAt: date('begin_at').defaultNow().notNull(),
endAt: date('end_at').defaultNow().notNull(), endAt: date('end_at').defaultNow().notNull(),
createdAt: timestamp('created_at').defaultNow().notNull() createdAt: timestamp('created_at').defaultNow().notNull(),
}) })
export const announcements = pgTable('announcements', { export const announcements = pgTable('announcements', {
id: serial('id').primaryKey(), id: serial('id').primaryKey(),
content: text('content').default('').notNull(), content: text('content').default('').notNull(),
createdAt: timestamp('created_at').defaultNow().notNull() createdAt: timestamp('created_at').defaultNow().notNull(),
}) })
export const posts = pgTable('posts', { export const posts = pgTable('posts', {
@@ -23,7 +23,7 @@ export const posts = pgTable('posts', {
slug: text('slug').notNull(), slug: text('slug').notNull(),
likes: integer('likes').default(0).notNull(), likes: integer('likes').default(0).notNull(),
views: integer('views').default(0).notNull(), views: integer('views').default(0).notNull(),
createdAt: timestamp('created_at').defaultNow().notNull() createdAt: timestamp('created_at').defaultNow().notNull(),
}) })
export const suggestions = pgTable('suggestions', { export const suggestions = pgTable('suggestions', {
@@ -32,9 +32,9 @@ export const suggestions = pgTable('suggestions', {
content: text('content').notNull(), content: text('content').notNull(),
added: boolean('added').default(false).notNull(), added: boolean('added').default(false).notNull(),
createdAt: timestamp('created_at').defaultNow().notNull(), createdAt: timestamp('created_at').defaultNow().notNull(),
updatedAt: timestamp('updated_at').defaultNow().notNull() updatedAt: timestamp('updated_at').defaultNow().notNull(),
}, t => ({ }, t => ({
pk: primaryKey({columns: [t.id, t.email]}) pk: primaryKey({ columns: [t.id, t.email] }),
})) }))
export const guestbookMessages = pgTable('guestbook_messages', { export const guestbookMessages = pgTable('guestbook_messages', {
@@ -43,7 +43,7 @@ export const guestbookMessages = pgTable('guestbook_messages', {
email: text('email').notNull().unique(), email: text('email').notNull().unique(),
username: text('username').notNull(), username: text('username').notNull(),
image: text('image').notNull(), image: text('image').notNull(),
createdAt: timestamp('created_at').defaultNow().notNull() createdAt: timestamp('created_at').defaultNow().notNull(),
}) })
export const categoriesType = pgEnum('categoryType', ['talent', 'bookmark']) export const categoriesType = pgEnum('categoryType', ['talent', 'bookmark'])
@@ -53,7 +53,7 @@ export const categories = pgTable('categories', {
slug: text('slug').unique().notNull(), slug: text('slug').unique().notNull(),
name: text('name').notNull(), name: text('name').notNull(),
type: categoriesType('type').notNull(), type: categoriesType('type').notNull(),
createdAt: timestamp('created_at').defaultNow().notNull() createdAt: timestamp('created_at').defaultNow().notNull(),
}) })
export const talents = pgTable('talents', { export const talents = pgTable('talents', {
@@ -63,14 +63,14 @@ export const talents = pgTable('talents', {
website: text('website').default('').notNull(), website: text('website').default('').notNull(),
work: text('work').default('').notNull(), work: text('work').default('').notNull(),
favorite: boolean('favorite').default(false).notNull(), favorite: boolean('favorite').default(false).notNull(),
createdAt: timestamp('created_at').defaultNow().notNull() createdAt: timestamp('created_at').defaultNow().notNull(),
}) })
export const talentsToCategories = pgTable('talents_categories', { export const talentsToCategories = pgTable('talents_categories', {
talentId: integer('talent_id').notNull() talentId: integer('talent_id').notNull()
.references(() => talents.id, { onDelete: 'cascade' }), .references(() => talents.id, { onDelete: 'cascade' }),
categoryId: integer('category_id').notNull() categoryId: integer('category_id').notNull()
.references(() => categories.id, {onDelete: 'cascade'}) .references(() => categories.id, { onDelete: 'cascade' }),
}) })
export const bookmarks = pgTable('bookmarks', { export const bookmarks = pgTable('bookmarks', {
@@ -78,51 +78,51 @@ export const bookmarks = pgTable('bookmarks', {
name: text('name').notNull(), name: text('name').notNull(),
website: text('website').default('').notNull(), website: text('website').default('').notNull(),
favorite: boolean('favorite').default(false).notNull(), favorite: boolean('favorite').default(false).notNull(),
createdAt: timestamp('created_at').defaultNow().notNull() createdAt: timestamp('created_at').defaultNow().notNull(),
}) })
export const bookmarksToCategories = pgTable('bookmarks_categories', { export const bookmarksToCategories = pgTable('bookmarks_categories', {
bookmarkId: integer('bookmark_id').notNull() bookmarkId: integer('bookmark_id').notNull()
.references(() => bookmarks.id, { onDelete: 'cascade' }), .references(() => bookmarks.id, { onDelete: 'cascade' }),
categoryId: integer('category_id').notNull() categoryId: integer('category_id').notNull()
.references(() => categories.id, {onDelete: 'cascade'}) .references(() => categories.id, { onDelete: 'cascade' }),
}, t => ({ }, t => ({
pk: primaryKey({columns: [t.bookmarkId, t.categoryId]}) pk: primaryKey({ columns: [t.bookmarkId, t.categoryId] }),
})) }))
// R E L A T I O N S // R E L A T I O N S
export const talentsRelations = relations(talents, ({ many }) => ({ export const talentsRelations = relations(talents, ({ many }) => ({
talentCategories: many(talentsToCategories) talentCategories: many(talentsToCategories),
})) }))
export const bookmarksRelations = relations(bookmarks, ({ many }) => ({ export const bookmarksRelations = relations(bookmarks, ({ many }) => ({
bookmarkCategories: many(bookmarksToCategories) bookmarkCategories: many(bookmarksToCategories),
})) }))
export const categoriesRelations = relations(categories, ({ many }) => ({ export const categoriesRelations = relations(categories, ({ many }) => ({
talentsToCategories: many(talentsToCategories), talentsToCategories: many(talentsToCategories),
bookmarksToCategories: many(bookmarksToCategories) bookmarksToCategories: many(bookmarksToCategories),
})) }))
export const talentsToCategoriesRelations = relations(talentsToCategories, ({ one }) => ({ export const talentsToCategoriesRelations = relations(talentsToCategories, ({ one }) => ({
talent: one(talents, { talent: one(talents, {
references: [talents.id], references: [talents.id],
fields: [talentsToCategories.talentId] fields: [talentsToCategories.talentId],
}), }),
category: one(categories, { category: one(categories, {
references: [categories.id], references: [categories.id],
fields: [talentsToCategories.categoryId] fields: [talentsToCategories.categoryId],
}) }),
})) }))
export const bookmarksToCategoriesRelations = relations(bookmarksToCategories, ({ one }) => ({ export const bookmarksToCategoriesRelations = relations(bookmarksToCategories, ({ one }) => ({
bookmark: one(bookmarks, { bookmark: one(bookmarks, {
references: [bookmarks.id], references: [bookmarks.id],
fields: [bookmarksToCategories.bookmarkId] fields: [bookmarksToCategories.bookmarkId],
}), }),
category: one(categories, { category: one(categories, {
references: [categories.id], references: [categories.id],
fields: [bookmarksToCategories.categoryId] fields: [bookmarksToCategories.categoryId],
}) }),
})) }))

View File

@@ -1,19 +1,19 @@
export default oauth.githubEventHandler({ export default oauth.githubEventHandler({
config: { config: {
emailRequired: true emailRequired: true,
}, },
async onSuccess(event, {user}) { async onSuccess(event, { user }) {
await setUserSession(event, { await setUserSession(event, {
user: { user: {
email: user.email, email: user.email,
picture: user.avatar_url, picture: user.avatar_url,
username: String(user.name).trim(), username: String(user.name).trim(),
admin: user.email === process.env.NUXT_AUTH_ADMIN_EMAIL admin: user.email === process.env.NUXT_AUTH_ADMIN_EMAIL,
} },
}) })
return sendRedirect(event, getCookie(event, 'last-route') || '/') return sendRedirect(event, getCookie(event, 'last-route') || '/')
}, },
onError(error) { onError(error) {
console.error('GitHub OAuth error:', error) console.error('GitHub OAuth error:', error)
} },
}) })

View File

@@ -1,16 +1,16 @@
export default oauth.googleEventHandler({ export default oauth.googleEventHandler({
async onSuccess(event, {user}) { async onSuccess(event, { user }) {
await setUserSession(event, { await setUserSession(event, {
user: { user: {
email: user.email, email: user.email,
picture: user.picture, picture: user.picture,
username: String(user.name).trim(), username: String(user.name).trim(),
admin: user.email === process.env.NUXT_AUTH_ADMIN_EMAIL admin: user.email === process.env.NUXT_AUTH_ADMIN_EMAIL,
} },
}) })
return sendRedirect(event, getCookie(event, 'last-route') || '/') return sendRedirect(event, getCookie(event, 'last-route') || '/')
}, },
onError(error) { onError(error) {
console.error('Google OAuth error:', error) console.error('Google OAuth error:', error)
} },
}) })

View File

@@ -1,11 +1,10 @@
import { drizzle } from 'drizzle-orm/postgres-js' import {drizzle} from 'drizzle-orm/postgres-js'
import postgres from 'postgres' import postgres from 'postgres'
import * as schema from '../database/schema' import * as schema from '../database/schema'
export const tables = schema export const tables = schema
export { sql, eq, and, or, asc, desc, sum, inArray } from 'drizzle-orm' export { sql, eq, and, or, asc, desc, sum, inArray } from 'drizzle-orm'
// eslint-disable-next-line node/prefer-global/process
const connectionString = process.env.DATABASE_URL as string const connectionString = process.env.DATABASE_URL as string
const client = postgres(connectionString, { prepare: false }) const client = postgres(connectionString, { prepare: false })

View File

@@ -1,4 +1,4 @@
import type { RuntimeConfig } from 'nuxt/schema' import type {RuntimeConfig} from 'nuxt/schema'
interface WebhookContent { interface WebhookContent {
title: string title: string
@@ -17,12 +17,12 @@ export async function sendDiscordWebhookMessage(config: RuntimeConfig, content:
color: content.color, color: content.color,
url: 'https://arthurdanjou.fr/talents', url: 'https://arthurdanjou.fr/talents',
footer: { footer: {
text: 'Powered by Nuxt' text: 'Powered by Nuxt',
}, },
timestamp: new Date().toISOString() timestamp: new Date().toISOString(),
} },
], ],
username: 'ArtDanjRobot - Website' username: 'ArtDanjRobot - Website',
} },
}) })
} }

View File

@@ -1,4 +1,4 @@
import type { MarkdownParsedContent, MarkdownRoot, ParsedContent } from '@nuxt/content/dist/runtime/types' import type {MarkdownParsedContent, ParsedContent} from '@nuxt/content/dist/runtime/types'
export enum ColorsTheme { export enum ColorsTheme {
RED = 'red', RED = 'red',
@@ -184,7 +184,7 @@ export const navs = [
].flat() ].flat()
export const IDEs = [ export const IDEs = [
{name: 'Visual Studio Code', icon: 'i-skill-icons-vscode-light'}, { name: 'Visual Studio Code', icon: 'i-skill-icons-vscode-light' },
{name: 'IntelliJ IDEA Ultimate', icon: 'i-skill-icons-idea-light'}, { name: 'IntelliJ IDEA Ultimate', icon: 'i-skill-icons-idea-light' },
{name: 'WebStorm', icon: 'i-skill-icons-webstorm-light'} { name: 'WebStorm', icon: 'i-skill-icons-webstorm-light' },
] ]