mirror of
https://github.com/ArthurDanjou/website.git
synced 2026-01-14 12:14:42 +01:00
@@ -2,8 +2,8 @@
|
||||
defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: 'Uses Section title'
|
||||
}
|
||||
default: 'Uses Section title',
|
||||
},
|
||||
})
|
||||
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: 'Uses Slot title'
|
||||
}
|
||||
default: 'Uses Slot title',
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
defineProps({
|
||||
href: {
|
||||
type: String,
|
||||
default: ''
|
||||
default: '',
|
||||
},
|
||||
target: {
|
||||
type: String,
|
||||
default: undefined,
|
||||
required: false
|
||||
}
|
||||
required: false,
|
||||
},
|
||||
})
|
||||
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
@@ -19,7 +19,7 @@ watch(isDark, () => {
|
||||
:ui="{
|
||||
background: 'bg-white dark:bg-stone-900',
|
||||
ring: 'ring-1 ring-gray-200 dark:ring-stone-800',
|
||||
container: 'z-30'
|
||||
container: 'z-30',
|
||||
}"
|
||||
>
|
||||
<template #default="{ open }">
|
||||
@@ -55,9 +55,9 @@ watch(isDark, () => {
|
||||
color: {
|
||||
white: {
|
||||
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'"
|
||||
@click.stop.prevent="setColor(color)"
|
||||
|
||||
@@ -10,7 +10,7 @@ function formatDate(date: number) {
|
||||
|
||||
const CardUi = {
|
||||
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)
|
||||
|
||||
@@ -3,23 +3,23 @@ const socials = [
|
||||
{
|
||||
name: 'mail',
|
||||
icon: 'i-material-symbols-alternate-email',
|
||||
link: 'mailto:arthurdanjou@outlook.fr'
|
||||
link: 'mailto:arthurdanjou@outlook.fr',
|
||||
},
|
||||
{
|
||||
name: 'twitter',
|
||||
icon: 'i-ph-twitter-logo-bold',
|
||||
link: 'https://twitter.com/ArthurDanj'
|
||||
link: 'https://twitter.com/ArthurDanj',
|
||||
},
|
||||
{
|
||||
name: 'github',
|
||||
icon: 'i-ph-github-logo-bold',
|
||||
link: 'https://github.com/ArthurDanjou'
|
||||
link: 'https://github.com/ArthurDanjou',
|
||||
},
|
||||
{
|
||||
name: 'linkedin',
|
||||
icon: 'i-ph-linkedin-logo-bold',
|
||||
link: 'https://www.linkedin.com/in/arthurdanjou/'
|
||||
}
|
||||
link: 'https://www.linkedin.com/in/arthurdanjou/',
|
||||
},
|
||||
]
|
||||
</script>
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ const stats = await $fetch<Stats>('/api/stats')
|
||||
|
||||
const CardUi = {
|
||||
footer: { padding: 'px-4 py-2' },
|
||||
body: {base: 'h-full'}
|
||||
body: { base: 'h-full' },
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@ defineProps({
|
||||
startDate: String,
|
||||
endDate: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
function formatTodayDate(date: string) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import type {Education} from '~~/types'
|
||||
|
||||
defineProps({
|
||||
education: Object as PropType<Education>
|
||||
education: Object as PropType<Education>,
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import type {WorkExperience} from '~~/types'
|
||||
|
||||
defineProps({
|
||||
experience: Object as PropType<WorkExperience>
|
||||
experience: Object as PropType<WorkExperience>,
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import type {Skill} from '~~/types'
|
||||
|
||||
defineProps({
|
||||
skill: Object as PropType<Skill>
|
||||
skill: Object as PropType<Skill>,
|
||||
})
|
||||
|
||||
const { $colorMode } = useNuxtApp()
|
||||
|
||||
@@ -5,8 +5,8 @@ export default defineConfig({
|
||||
schema: './server/database/schema.ts',
|
||||
out: './server/database/migrations',
|
||||
dbCredentials: {
|
||||
connectionString: process.env.DATABASE_URL as string
|
||||
connectionString: process.env.DATABASE_URL as string,
|
||||
},
|
||||
strict: true,
|
||||
verbose: true
|
||||
verbose: true,
|
||||
})
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import antfu from '@antfu/eslint-config'
|
||||
|
||||
export default antfu()
|
||||
export default antfu({
|
||||
rules: {
|
||||
'node/prefer-global/process': 'off',
|
||||
},
|
||||
})
|
||||
|
||||
@@ -11,14 +11,14 @@ export default defineNuxtRouteMiddleware(async (to) => {
|
||||
|
||||
if (isMaintenance.value && to.path !== '/maintenance') {
|
||||
return navigateTo('/maintenance', {
|
||||
redirectCode: 301
|
||||
redirectCode: 301,
|
||||
})
|
||||
}
|
||||
|
||||
if (!isMaintenance.value && to.path === '/maintenance') {
|
||||
return navigateTo('/', {
|
||||
redirectCode: 301,
|
||||
replace: true
|
||||
replace: true,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable node/prefer-global/process */
|
||||
export default defineNuxtRouteMiddleware((to) => {
|
||||
if (to.path === '/writing' && process.env.NODE_ENV !== 'development') {
|
||||
return navigateTo('/', {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { addCustomTab } from '@nuxt/devtools-kit'
|
||||
export default defineNuxtModule({
|
||||
meta: {
|
||||
name: 'drizzle-studio',
|
||||
version: '0.0.1'
|
||||
version: '0.0.1',
|
||||
},
|
||||
setup() {
|
||||
addCustomTab({
|
||||
@@ -13,8 +13,8 @@ export default defineNuxtModule({
|
||||
icon: 'simple-icons:drizzle',
|
||||
view: {
|
||||
type: 'iframe',
|
||||
src: 'https://local.drizzle.studio/?themeId=azX2nOTScT9U6SWEmlq7z'
|
||||
}
|
||||
src: 'https://local.drizzle.studio/?themeId=azX2nOTScT9U6SWEmlq7z',
|
||||
},
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable node/prefer-global/process */
|
||||
export default defineNuxtConfig({
|
||||
css: [
|
||||
'@/assets/css/main.scss',
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"db:studio": "prisma studio --browser none",
|
||||
"db:studio": "drizzle-kit studio",
|
||||
"db:generate": "drizzle-kit generate:pg",
|
||||
"build": "nuxt build",
|
||||
"dev": "nuxt dev --host",
|
||||
"generate": "nuxt generate",
|
||||
"preview": "nuxt preview",
|
||||
"postinstall": "nuxt prepare",
|
||||
"postinstall": "nuxt prepare && drizzle-kit generate:pg",
|
||||
"lint": "eslint .",
|
||||
"lint:fix": "eslint . --fix"
|
||||
},
|
||||
@@ -22,6 +23,7 @@
|
||||
"drizzle-orm": "0.30.8",
|
||||
"nuxt": "3.10.3",
|
||||
"nuxt-auth-utils": "0.0.20",
|
||||
"pg": "^8.11.5",
|
||||
"pinia": "2.1.7",
|
||||
"postcss-custom-properties": "13.3.7",
|
||||
"postgres": "3.4.4",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
useHead({
|
||||
title: 'About me • Arthur Danjou'
|
||||
title: 'About me • Arthur Danjou',
|
||||
})
|
||||
|
||||
const { data: skills } = await getSkills()
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import {useBookmarksStore} from '~/store/bookmarks'
|
||||
|
||||
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' }])
|
||||
@@ -12,9 +12,9 @@ const { data: bookmarks, pending } = await useFetch('/api/bookmarks', {
|
||||
method: 'get',
|
||||
query: {
|
||||
favorite: isFavorite,
|
||||
category: getCategory
|
||||
category: getCategory,
|
||||
},
|
||||
watch: [isFavorite, getCategory]
|
||||
watch: [isFavorite, getCategory],
|
||||
})
|
||||
|
||||
const { data: getCategories } = await useFetch('/api/categories', { method: 'GET', query: { type: 'bookmark' } })
|
||||
@@ -30,7 +30,7 @@ const getMarkerStyle = computed(() => {
|
||||
top: `${selected?.offsetTop}px`,
|
||||
left: `${selected?.offsetLeft === 12 ? 4 : selected?.offsetLeft}px`,
|
||||
height: `${selected?.offsetHeight}px`,
|
||||
width: `${selected?.offsetWidth}px`
|
||||
width: `${selected?.offsetWidth}px`,
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import {providers} from '~~/types'
|
||||
|
||||
useHead({
|
||||
title: 'Sign my guestbook • Arthur Danjou'
|
||||
title: 'Sign my guestbook • Arthur Danjou',
|
||||
})
|
||||
|
||||
const { loggedIn, clear, user } = useUserSession()
|
||||
@@ -20,20 +20,20 @@ async function sign() {
|
||||
await $fetch('/api/message', {
|
||||
method: 'post',
|
||||
body: {
|
||||
message: messageContent.value
|
||||
}
|
||||
message: messageContent.value,
|
||||
},
|
||||
}).then(async () => {
|
||||
toast.add({
|
||||
title: `Thanks for leaving a message!`,
|
||||
description: 'Your can see it at the top of the messages.',
|
||||
icon: 'i-material-symbols-check-circle-outline-rounded',
|
||||
timeout: 4000
|
||||
timeout: 4000,
|
||||
})
|
||||
await refresh()
|
||||
}).catch(() => {
|
||||
toast.add({
|
||||
title: 'An error occurred when signing the book!',
|
||||
color: 'red'
|
||||
color: 'red',
|
||||
})
|
||||
})
|
||||
messageContent.value = ''
|
||||
@@ -45,20 +45,20 @@ async function deleteMessage(id: number) {
|
||||
await $fetch('/api/message', {
|
||||
method: 'delete',
|
||||
body: {
|
||||
id
|
||||
}
|
||||
id,
|
||||
},
|
||||
}).then(async () => {
|
||||
toast.add({
|
||||
title: `Message successfully deleted`,
|
||||
icon: 'i-material-symbols-check-circle-outline-rounded',
|
||||
color: 'green',
|
||||
timeout: 4000
|
||||
timeout: 4000,
|
||||
})
|
||||
await refresh()
|
||||
}).catch(() => {
|
||||
toast.add({
|
||||
title: 'An error occured when deleting a message!',
|
||||
color: 'red'
|
||||
color: 'red',
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
useHead({
|
||||
title: 'Arthur Danjou • Software Engineer and Maths Lover'
|
||||
title: 'Arthur Danjou • Software Engineer and Maths Lover',
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<script lang="ts" setup>
|
||||
definePageMeta({
|
||||
layout: 'maintenance'
|
||||
layout: 'maintenance',
|
||||
})
|
||||
|
||||
useHead({
|
||||
title: 'Site under maintenance • Arthur Danjou'
|
||||
title: 'Site under maintenance • Arthur Danjou',
|
||||
})
|
||||
|
||||
const { data: maintenance } = await useFetch('/api/maintenance')
|
||||
@@ -17,23 +17,23 @@ const socials = [
|
||||
{
|
||||
name: 'mail',
|
||||
icon: 'i-material-symbols-alternate-email',
|
||||
link: 'mailto:arthurdanjou@outlook.fr'
|
||||
link: 'mailto:arthurdanjou@outlook.fr',
|
||||
},
|
||||
{
|
||||
name: 'twitter',
|
||||
icon: 'i-ph-twitter-logo-bold',
|
||||
link: 'https://twitter.com/ArthurDanj'
|
||||
link: 'https://twitter.com/ArthurDanj',
|
||||
},
|
||||
{
|
||||
name: 'github',
|
||||
icon: 'i-ph-github-logo-bold',
|
||||
link: 'https://github.com/ArthurDanjou'
|
||||
link: 'https://github.com/ArthurDanjou',
|
||||
},
|
||||
{
|
||||
name: 'linkedin',
|
||||
icon: 'i-ph-linkedin-logo-bold',
|
||||
link: 'https://www.linkedin.com/in/arthurdanjou/'
|
||||
}
|
||||
link: 'https://www.linkedin.com/in/arthurdanjou/',
|
||||
},
|
||||
]
|
||||
</script>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useTalentsStore } from '~/store/talents'
|
||||
import {providers} from '~~/types'
|
||||
|
||||
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 }])
|
||||
@@ -14,16 +14,16 @@ const { data: talents, pending } = await useFetch('/api/talents', {
|
||||
method: 'get',
|
||||
query: {
|
||||
favorite: isFavorite,
|
||||
category: getCategory
|
||||
category: getCategory,
|
||||
},
|
||||
watch: [isFavorite, getCategory]
|
||||
watch: [isFavorite, getCategory],
|
||||
})
|
||||
|
||||
const { data: getCategories } = await useFetch('/api/categories', { method: 'GET', query: { type: 'talent' } })
|
||||
getCategories.value!.forEach(category => categories.value.push({
|
||||
label: category.name,
|
||||
slug: category.slug,
|
||||
id: category.id
|
||||
id: category.id,
|
||||
}))
|
||||
|
||||
function isCategory(slug: string) {
|
||||
@@ -36,7 +36,7 @@ const getMarkerStyle = computed(() => {
|
||||
top: `${selected?.offsetTop}px`,
|
||||
left: `${selected?.offsetLeft === 12 ? 4 : selected?.offsetLeft}px`,
|
||||
height: `${selected?.offsetHeight}px`,
|
||||
width: `${selected?.offsetWidth}px`
|
||||
width: `${selected?.offsetWidth}px`,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -56,19 +56,19 @@ async function suggest() {
|
||||
await $fetch('/api/suggestion', {
|
||||
method: 'post',
|
||||
body: {
|
||||
content: suggestContent.value
|
||||
}
|
||||
content: suggestContent.value,
|
||||
},
|
||||
}).then((response) => {
|
||||
toast.add({
|
||||
title: `Your suggestion for '${response[0].content}' has been successfully added`,
|
||||
color: 'green',
|
||||
icon: 'i-material-symbols-check-circle-outline-rounded',
|
||||
timeout: 4000
|
||||
timeout: 4000,
|
||||
})
|
||||
}).catch(() => {
|
||||
toast.add({
|
||||
title: 'An error occurred when suggesting someone',
|
||||
color: 'red'
|
||||
color: 'red',
|
||||
})
|
||||
})
|
||||
suggestContent.value = ''
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
useHead({
|
||||
title: 'My work • Arthur Danjou'
|
||||
title: 'My work • Arthur Danjou',
|
||||
})
|
||||
const { data: projects } = await getProjects()
|
||||
</script>
|
||||
|
||||
@@ -7,12 +7,12 @@ 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
|
||||
data: post,
|
||||
} = await useFetch<PrismaPost>('/api/article', {
|
||||
method: 'post',
|
||||
body: {
|
||||
slug: route.params.slug.toString()
|
||||
}
|
||||
slug: route.params.slug.toString(),
|
||||
},
|
||||
})
|
||||
|
||||
const likes = ref(post.value?.likes)
|
||||
@@ -20,8 +20,8 @@ async function like() {
|
||||
const data = await $fetch<PrismaPost>('/api/like', {
|
||||
method: 'PUT',
|
||||
body: {
|
||||
slug: post.value?.slug
|
||||
}
|
||||
slug: post.value?.slug,
|
||||
},
|
||||
})
|
||||
likes.value = data.likes
|
||||
}
|
||||
@@ -29,30 +29,30 @@ async function like() {
|
||||
if (!postContent.value) {
|
||||
throw showError({
|
||||
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('"', '')
|
||||
useHead({
|
||||
title: `${postContent.value?.title} • Arthur Danjou's shelf`
|
||||
title: `${postContent.value?.title} • Arthur Danjou's shelf`,
|
||||
})
|
||||
|
||||
function top() {
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
left: 0,
|
||||
behavior: 'smooth'
|
||||
behavior: 'smooth',
|
||||
})
|
||||
}
|
||||
|
||||
const { copy, copied } = useClipboard({
|
||||
source: `https://arthurdanjou.fr/writing/${route.params.slug}`,
|
||||
copiedDuring: 4000
|
||||
copiedDuring: 4000,
|
||||
})
|
||||
|
||||
const likeCookie = useCookie<boolean>(`post:like:${postContent.value.slug}`, {
|
||||
maxAge: 604_800
|
||||
maxAge: 604_800,
|
||||
})
|
||||
|
||||
async function handleLike() {
|
||||
|
||||
@@ -3,7 +3,7 @@ const appConfig = useAppConfig()
|
||||
const getColor = computed(() => `text-${appConfig.ui.primary}-500`)
|
||||
|
||||
useHead({
|
||||
title: 'My Shelf • Arthur Danjou'
|
||||
title: 'My Shelf • Arthur Danjou',
|
||||
})
|
||||
|
||||
const { data: posts } = await getPosts()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export default defineEventHandler(async () => {
|
||||
return useDB().query.announcements.findFirst({
|
||||
orderBy: (announcement, {asc}) => [asc(announcement.createdAt)]
|
||||
orderBy: (announcement, { asc }) => [asc(announcement.createdAt)],
|
||||
})
|
||||
})
|
||||
|
||||
@@ -5,12 +5,12 @@ const PostSchema = z.object({ slug: z.string() }).parse
|
||||
export default defineEventHandler(async (event) => {
|
||||
const { slug } = await readValidatedBody(event, PostSchema)
|
||||
return useDB().insert(tables.posts).values({
|
||||
slug
|
||||
slug,
|
||||
}).onConflictDoUpdate({
|
||||
target: tables.posts.id,
|
||||
set: {
|
||||
views: sql`${tables.posts.views}
|
||||
+ 1`
|
||||
}
|
||||
+ 1`,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
@@ -7,14 +7,14 @@ export default defineEventHandler(async (event) => {
|
||||
with: {
|
||||
bookmarkCategories: {
|
||||
with: {
|
||||
category: true
|
||||
}
|
||||
}
|
||||
}
|
||||
category: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return bookmarks.filter(bookmark =>
|
||||
(category === 'all' || bookmark.bookmarkCategories.some(cat => cat.category.slug === category))
|
||||
&& (favorite === 'false' || bookmark.favorite)
|
||||
&& (favorite === 'false' || bookmark.favorite),
|
||||
)
|
||||
})
|
||||
|
||||
@@ -7,7 +7,7 @@ export default defineEventHandler(async (event) => {
|
||||
return useDB().update(tables.posts)
|
||||
.set({
|
||||
likes: sql`${tables.posts.likes}
|
||||
+ 1`
|
||||
+ 1`,
|
||||
})
|
||||
.where(eq(tables.posts.slug, slug))
|
||||
})
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
export default defineEventHandler(async () => {
|
||||
const maintenance = await useDB().query.maintenances.findFirst({
|
||||
orderBy: [asc(tables.maintenances.createdAt)]
|
||||
orderBy: [asc(tables.maintenances.createdAt)],
|
||||
})
|
||||
let enabled = true
|
||||
// eslint-disable-next-line node/prefer-global/process
|
||||
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
enabled = false
|
||||
}
|
||||
@@ -17,6 +17,6 @@ export default defineEventHandler(async () => {
|
||||
|
||||
return {
|
||||
enabled,
|
||||
maintenance
|
||||
maintenance,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {z} from 'zod'
|
||||
|
||||
const MessageValidator = z.object({
|
||||
id: z.number()
|
||||
id: z.number(),
|
||||
}).parse
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {z} from 'zod'
|
||||
|
||||
const MessageValidator = z.object({
|
||||
message: z.string()
|
||||
message: z.string(),
|
||||
}).parse
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
@@ -12,19 +12,19 @@ export default defineEventHandler(async (event) => {
|
||||
await sendDiscordWebhookMessage(config, {
|
||||
title: 'New guestbook message ✨',
|
||||
description: `**${user.username}** has signed the book : "*${message}*"`,
|
||||
color: 15893567
|
||||
color: 15893567,
|
||||
})
|
||||
return useDB().insert(tables.guestbookMessages)
|
||||
.values({
|
||||
message,
|
||||
email: user.email,
|
||||
username: user.username,
|
||||
image: user.picture
|
||||
image: user.picture,
|
||||
})
|
||||
.onConflictDoUpdate({
|
||||
target: tables.guestbookMessages.email,
|
||||
set: {
|
||||
message
|
||||
}
|
||||
message,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
@@ -8,8 +8,8 @@ export default defineCachedEventHandler(async (event) => {
|
||||
coding,
|
||||
editors,
|
||||
os,
|
||||
languages
|
||||
languages,
|
||||
}
|
||||
}, {
|
||||
maxAge: 60 * 60 * 3 // 3 hours,
|
||||
maxAge: 60 * 60 * 3, // 3 hours,
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {z} from 'zod'
|
||||
|
||||
const SuggestionValidator = z.object({
|
||||
content: z.string()
|
||||
content: z.string(),
|
||||
}).parse
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
@@ -12,23 +12,23 @@ export default defineEventHandler(async (event) => {
|
||||
await sendDiscordWebhookMessage(config, {
|
||||
title: 'New suggestion ✨',
|
||||
description: `**${user.username}** has requested **${content}** for the talents page.`,
|
||||
color: 15237114
|
||||
color: 15237114,
|
||||
})
|
||||
|
||||
return useDB().insert(tables.suggestions)
|
||||
.values({
|
||||
email: user.email,
|
||||
content
|
||||
content,
|
||||
})
|
||||
.onConflictDoUpdate({
|
||||
target: tables.suggestions.email,
|
||||
set: {
|
||||
content
|
||||
content,
|
||||
},
|
||||
setWhere: sql`${tables.suggestions.email}
|
||||
=
|
||||
${user.email}`
|
||||
${user.email}`,
|
||||
}).returning({
|
||||
content: tables.suggestions.content
|
||||
content: tables.suggestions.content,
|
||||
})
|
||||
})
|
||||
|
||||
@@ -7,14 +7,14 @@ export default defineEventHandler(async (event) => {
|
||||
with: {
|
||||
talentCategories: {
|
||||
with: {
|
||||
category: true
|
||||
}
|
||||
}
|
||||
}
|
||||
category: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return talents.filter(talent =>
|
||||
(category === 'all' || talent.talentCategories.some(cat => cat.category.slug === category))
|
||||
&& (favorite === 'false' || talent.favorite)
|
||||
&& (favorite === 'false' || talent.favorite),
|
||||
)
|
||||
})
|
||||
|
||||
@@ -7,7 +7,7 @@ export default defineEventHandler(async (event) => {
|
||||
return useDB().update(tables.posts)
|
||||
.set({
|
||||
views: sql`${tables.posts.views}
|
||||
+ 1`
|
||||
+ 1`,
|
||||
})
|
||||
.where(eq(tables.posts.slug, slug))
|
||||
})
|
||||
|
||||
@@ -9,13 +9,13 @@ export const maintenances = pgTable('maintenances', {
|
||||
enabled: boolean('enabled').default(false).notNull(),
|
||||
beginAt: date('begin_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', {
|
||||
id: serial('id').primaryKey(),
|
||||
content: text('content').default('').notNull(),
|
||||
createdAt: timestamp('created_at').defaultNow().notNull()
|
||||
createdAt: timestamp('created_at').defaultNow().notNull(),
|
||||
})
|
||||
|
||||
export const posts = pgTable('posts', {
|
||||
@@ -23,7 +23,7 @@ export const posts = pgTable('posts', {
|
||||
slug: text('slug').notNull(),
|
||||
likes: integer('likes').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', {
|
||||
@@ -32,9 +32,9 @@ export const suggestions = pgTable('suggestions', {
|
||||
content: text('content').notNull(),
|
||||
added: boolean('added').default(false).notNull(),
|
||||
createdAt: timestamp('created_at').defaultNow().notNull(),
|
||||
updatedAt: timestamp('updated_at').defaultNow().notNull()
|
||||
updatedAt: timestamp('updated_at').defaultNow().notNull(),
|
||||
}, t => ({
|
||||
pk: primaryKey({columns: [t.id, t.email]})
|
||||
pk: primaryKey({ columns: [t.id, t.email] }),
|
||||
}))
|
||||
|
||||
export const guestbookMessages = pgTable('guestbook_messages', {
|
||||
@@ -43,7 +43,7 @@ export const guestbookMessages = pgTable('guestbook_messages', {
|
||||
email: text('email').notNull().unique(),
|
||||
username: text('username').notNull(),
|
||||
image: text('image').notNull(),
|
||||
createdAt: timestamp('created_at').defaultNow().notNull()
|
||||
createdAt: timestamp('created_at').defaultNow().notNull(),
|
||||
})
|
||||
|
||||
export const categoriesType = pgEnum('categoryType', ['talent', 'bookmark'])
|
||||
@@ -53,7 +53,7 @@ export const categories = pgTable('categories', {
|
||||
slug: text('slug').unique().notNull(),
|
||||
name: text('name').notNull(),
|
||||
type: categoriesType('type').notNull(),
|
||||
createdAt: timestamp('created_at').defaultNow().notNull()
|
||||
createdAt: timestamp('created_at').defaultNow().notNull(),
|
||||
})
|
||||
|
||||
export const talents = pgTable('talents', {
|
||||
@@ -63,14 +63,14 @@ export const talents = pgTable('talents', {
|
||||
website: text('website').default('').notNull(),
|
||||
work: text('work').default('').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', {
|
||||
talentId: integer('talent_id').notNull()
|
||||
.references(() => talents.id, { onDelete: 'cascade' }),
|
||||
categoryId: integer('category_id').notNull()
|
||||
.references(() => categories.id, {onDelete: 'cascade'})
|
||||
.references(() => categories.id, { onDelete: 'cascade' }),
|
||||
})
|
||||
|
||||
export const bookmarks = pgTable('bookmarks', {
|
||||
@@ -78,51 +78,51 @@ export const bookmarks = pgTable('bookmarks', {
|
||||
name: text('name').notNull(),
|
||||
website: text('website').default('').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', {
|
||||
bookmarkId: integer('bookmark_id').notNull()
|
||||
.references(() => bookmarks.id, { onDelete: 'cascade' }),
|
||||
categoryId: integer('category_id').notNull()
|
||||
.references(() => categories.id, {onDelete: 'cascade'})
|
||||
.references(() => categories.id, { onDelete: 'cascade' }),
|
||||
}, t => ({
|
||||
pk: primaryKey({columns: [t.bookmarkId, t.categoryId]})
|
||||
pk: primaryKey({ columns: [t.bookmarkId, t.categoryId] }),
|
||||
}))
|
||||
|
||||
// R E L A T I O N S
|
||||
|
||||
export const talentsRelations = relations(talents, ({ many }) => ({
|
||||
talentCategories: many(talentsToCategories)
|
||||
talentCategories: many(talentsToCategories),
|
||||
}))
|
||||
|
||||
export const bookmarksRelations = relations(bookmarks, ({ many }) => ({
|
||||
bookmarkCategories: many(bookmarksToCategories)
|
||||
bookmarkCategories: many(bookmarksToCategories),
|
||||
}))
|
||||
|
||||
export const categoriesRelations = relations(categories, ({ many }) => ({
|
||||
talentsToCategories: many(talentsToCategories),
|
||||
bookmarksToCategories: many(bookmarksToCategories)
|
||||
bookmarksToCategories: many(bookmarksToCategories),
|
||||
}))
|
||||
|
||||
export const talentsToCategoriesRelations = relations(talentsToCategories, ({ one }) => ({
|
||||
talent: one(talents, {
|
||||
references: [talents.id],
|
||||
fields: [talentsToCategories.talentId]
|
||||
fields: [talentsToCategories.talentId],
|
||||
}),
|
||||
category: one(categories, {
|
||||
references: [categories.id],
|
||||
fields: [talentsToCategories.categoryId]
|
||||
})
|
||||
fields: [talentsToCategories.categoryId],
|
||||
}),
|
||||
}))
|
||||
|
||||
export const bookmarksToCategoriesRelations = relations(bookmarksToCategories, ({ one }) => ({
|
||||
bookmark: one(bookmarks, {
|
||||
references: [bookmarks.id],
|
||||
fields: [bookmarksToCategories.bookmarkId]
|
||||
fields: [bookmarksToCategories.bookmarkId],
|
||||
}),
|
||||
category: one(categories, {
|
||||
references: [categories.id],
|
||||
fields: [bookmarksToCategories.categoryId]
|
||||
})
|
||||
fields: [bookmarksToCategories.categoryId],
|
||||
}),
|
||||
}))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export default oauth.githubEventHandler({
|
||||
config: {
|
||||
emailRequired: true
|
||||
emailRequired: true,
|
||||
},
|
||||
async onSuccess(event, { user }) {
|
||||
await setUserSession(event, {
|
||||
@@ -8,12 +8,12 @@ export default oauth.githubEventHandler({
|
||||
email: user.email,
|
||||
picture: user.avatar_url,
|
||||
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') || '/')
|
||||
},
|
||||
onError(error) {
|
||||
console.error('GitHub OAuth error:', error)
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
@@ -5,12 +5,12 @@ export default oauth.googleEventHandler({
|
||||
email: user.email,
|
||||
picture: user.picture,
|
||||
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') || '/')
|
||||
},
|
||||
onError(error) {
|
||||
console.error('Google OAuth error:', error)
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
@@ -5,7 +5,6 @@ import * as schema from '../database/schema'
|
||||
export const tables = schema
|
||||
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 client = postgres(connectionString, { prepare: false })
|
||||
|
||||
|
||||
@@ -17,12 +17,12 @@ export async function sendDiscordWebhookMessage(config: RuntimeConfig, content:
|
||||
color: content.color,
|
||||
url: 'https://arthurdanjou.fr/talents',
|
||||
footer: {
|
||||
text: 'Powered by Nuxt'
|
||||
text: 'Powered by Nuxt',
|
||||
},
|
||||
timestamp: new Date().toISOString(),
|
||||
},
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
],
|
||||
username: 'ArtDanjRobot - Website'
|
||||
}
|
||||
username: 'ArtDanjRobot - Website',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
4
types.ts
4
types.ts
@@ -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 {
|
||||
RED = 'red',
|
||||
@@ -186,5 +186,5 @@ export const navs = [
|
||||
export const IDEs = [
|
||||
{ name: 'Visual Studio Code', icon: 'i-skill-icons-vscode-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' },
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user