mirror of
https://github.com/ArthurDanjou/artsite.git
synced 2026-01-14 18:59:59 +01:00
feat: ajouter de nouveaux gestionnaires d'événements pour les API et supprimer les fichiers obsolètes
This commit is contained in:
4
server/api/activity.get.ts
Normal file
4
server/api/activity.get.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export default defineEventHandler(async (event) => {
|
||||
const { discord } = useRuntimeConfig(event)
|
||||
return await $fetch(`https://api.lanyard.rest/v1/users/${discord.userId}`)
|
||||
})
|
||||
@@ -1,89 +0,0 @@
|
||||
import { defineEventHandler, readValidatedBody } from 'h3'
|
||||
import { z } from 'zod'
|
||||
import {
|
||||
convertToModelMessages,
|
||||
createUIMessageStream,
|
||||
createUIMessageStreamResponse,
|
||||
streamText
|
||||
} from 'ai'
|
||||
import type { UIMessage } from 'ai'
|
||||
import { createWorkersAI } from 'workers-ai-provider'
|
||||
import { statsTool } from '~~/shared/utils/tools/stats'
|
||||
import { activityTool } from '~~/shared/utils/tools/activity'
|
||||
import { resumesTool } from '~~/shared/utils/tools/read-resume'
|
||||
import { resourceTool } from '~~/shared/utils/tools/read-resources'
|
||||
import { statusPageTool } from '~~/shared/utils/tools/status-page'
|
||||
import { usesByCategoryTool } from '~~/shared/utils/tools/uses-by-category'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const { messages, lang } = await readValidatedBody(event, z.object({
|
||||
messages: z.array(z.custom<UIMessage>()),
|
||||
lang: z.string().optional()
|
||||
}).parse)
|
||||
|
||||
const { AI } = event.context.cloudflare.env || {}
|
||||
|
||||
if (!AI) {
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
statusMessage: 'Cloudflare AI Binding not found. Check wrangler.json.'
|
||||
})
|
||||
}
|
||||
|
||||
const validCategories = ['contact', 'education', 'experiences', 'hobbies', 'languages', 'profile', 'projects', 'skills', 'uses'].join(', ')
|
||||
|
||||
const workersAI = createWorkersAI({ binding: AI })
|
||||
|
||||
const stream = createUIMessageStream({
|
||||
execute: async ({ writer }) => {
|
||||
const result = streamText({
|
||||
model: workersAI('@cf/meta/llama-3-8b-instruct'),
|
||||
system: `You are Arthur Danjou's personal portfolio assistant (Data Science Student).
|
||||
|
||||
### TOOL USAGE GUIDE - FOLLOW STRICTLY:
|
||||
|
||||
1. **readResources(category)**: YOUR PRIMARY BRAIN.
|
||||
- Use this for ANY question about Arthur's life, background, or work.
|
||||
- Categories: ${validCategories}.
|
||||
- Example: "What are his skills?" -> call readResources('skills').
|
||||
- Example: "Where did he study?" -> call readResources('education').
|
||||
|
||||
2. **readResume()**: THE FILE DISPENSER.
|
||||
- **WARNING:** This tool ONLY returns a direct DOWNLOAD LINK (URL). It does NOT contain text.
|
||||
- **WHEN TO USE:** ONLY when the user explicitly asks to "download", "get", "see", or "have" the CV/Resume file.
|
||||
- **DO NOT USE:** If the user asks "What is on his resume?" or "Describe his experience", DO NOT use this. Use 'readResources' instead.
|
||||
|
||||
3. **stats()**: GITHUB METRICS.
|
||||
- Use this for questions about coding volume, commit streaks, languages used percentages, or GitHub activity.
|
||||
|
||||
4. **activity()**: LIVE STATUS.
|
||||
- Use this to know what Arthur is doing RIGHT NOW (e.g., "Is he coding?", "What is he listening to on Spotify?").
|
||||
|
||||
5. **statusPage()**: INFRASTRUCTURE HEALTH.
|
||||
- Use this if the user asks about the website's uptime, server status, or if services are down.
|
||||
|
||||
6. **usesByCategory()**: TECH STACK & GEAR.
|
||||
- Use this for specific questions about his hardware (Mac vs PC), software, developer tools, or desk setup.
|
||||
|
||||
### GENERAL RULES:
|
||||
- If you don't call a tool, you know NOTHING. Do not hallucinate.
|
||||
- Always answer in ${lang || 'English'}.
|
||||
- Be concise and professional.
|
||||
`,
|
||||
tools: {
|
||||
stats: statsTool,
|
||||
activity: activityTool,
|
||||
readResume: resumesTool,
|
||||
readResources: resourceTool,
|
||||
statusPage: statusPageTool,
|
||||
usesByCategory: usesByCategoryTool
|
||||
},
|
||||
messages: await convertToModelMessages(messages)
|
||||
})
|
||||
|
||||
writer.merge(result.toUIMessageStream())
|
||||
}
|
||||
})
|
||||
|
||||
return createUIMessageStreamResponse({ stream })
|
||||
})
|
||||
16
server/api/contact.get.ts
Normal file
16
server/api/contact.get.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { queryCollection } from '@nuxt/content/server'
|
||||
|
||||
export default defineCachedEventHandler(async (event) => {
|
||||
const result = await queryCollection(event, 'contact')
|
||||
.where('extension', '=', 'json')
|
||||
.first()
|
||||
|
||||
if (!result) {
|
||||
throw createError({ statusCode: 404, statusMessage: 'Contact information not found' })
|
||||
}
|
||||
|
||||
return result.body
|
||||
}, {
|
||||
maxAge: 60 * 60 * 24,
|
||||
name: 'contact'
|
||||
})
|
||||
24
server/api/education.get.ts
Normal file
24
server/api/education.get.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { queryCollection } from '@nuxt/content/server'
|
||||
|
||||
export default defineCachedEventHandler(async (event) => {
|
||||
const result = await queryCollection(event, 'education')
|
||||
.where('extension', '=', 'md')
|
||||
.all()
|
||||
|
||||
if (result.length === 0) {
|
||||
throw createError({ statusCode: 404, statusMessage: 'Education records not found' })
|
||||
}
|
||||
|
||||
return result
|
||||
.sort((a, b) => new Date(b.startDate).getTime() - new Date(a.startDate).getTime())
|
||||
.map(edu => ({
|
||||
degree: edu.degree,
|
||||
institution: edu.institution,
|
||||
startDate: edu.startDate,
|
||||
endDate: edu.endDate,
|
||||
location: edu.location
|
||||
}))
|
||||
}, {
|
||||
maxAge: 60 * 60 * 24,
|
||||
name: 'education'
|
||||
})
|
||||
27
server/api/experiences.get.ts
Normal file
27
server/api/experiences.get.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { queryCollection } from '@nuxt/content/server'
|
||||
|
||||
export default defineCachedEventHandler(async (event) => {
|
||||
const result = await queryCollection(event, 'experiences')
|
||||
.where('extension', '=', 'md')
|
||||
.all()
|
||||
|
||||
if (result.length === 0) {
|
||||
throw createError({ statusCode: 404, statusMessage: 'Experience records not found' })
|
||||
}
|
||||
|
||||
return result
|
||||
.sort((a, b) => new Date(b.startDate).getTime() - new Date(a.startDate).getTime())
|
||||
.map(exp => ({
|
||||
title: exp.title,
|
||||
company: exp.company,
|
||||
companyUrl: exp.companyUrl,
|
||||
startDate: exp.startDate,
|
||||
endDate: exp.endDate,
|
||||
location: exp.location,
|
||||
description: exp.description
|
||||
}))
|
||||
},
|
||||
{
|
||||
maxAge: 60 * 60 * 24,
|
||||
name: 'experiences'
|
||||
})
|
||||
15
server/api/hobbies.get.ts
Normal file
15
server/api/hobbies.get.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { queryCollection } from '@nuxt/content/server'
|
||||
|
||||
export default defineCachedEventHandler(async (event) => {
|
||||
const result = await queryCollection(event, 'hobbies')
|
||||
.where('extension', '=', 'md')
|
||||
.first()
|
||||
if (!result) {
|
||||
throw createError({ statusCode: 404, statusMessage: 'Hobbies not found' })
|
||||
}
|
||||
|
||||
return result.body
|
||||
}, {
|
||||
maxAge: 60 * 60 * 24,
|
||||
name: 'hobbies'
|
||||
})
|
||||
16
server/api/languages.get.ts
Normal file
16
server/api/languages.get.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { queryCollection } from '@nuxt/content/server'
|
||||
|
||||
export default defineCachedEventHandler(async (event) => {
|
||||
const result = await queryCollection(event, 'languages')
|
||||
.where('extension', '=', 'json')
|
||||
.first()
|
||||
|
||||
if (!result) {
|
||||
throw createError({ statusCode: 404, statusMessage: 'Languages not found' })
|
||||
}
|
||||
|
||||
return result.body
|
||||
}, {
|
||||
maxAge: 60 * 60 * 24,
|
||||
name: 'languages'
|
||||
})
|
||||
16
server/api/profile.get.ts
Normal file
16
server/api/profile.get.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { queryCollection } from '@nuxt/content/server'
|
||||
|
||||
export default defineCachedEventHandler(async (event) => {
|
||||
const result = await queryCollection(event, 'profile')
|
||||
.where('extension', '=', 'md')
|
||||
.first()
|
||||
|
||||
if (!result) {
|
||||
throw createError({ statusCode: 404, statusMessage: 'Profile not found' })
|
||||
}
|
||||
|
||||
return result
|
||||
}, {
|
||||
maxAge: 60 * 60 * 24,
|
||||
name: 'profile'
|
||||
})
|
||||
16
server/api/projects.get.ts
Normal file
16
server/api/projects.get.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { queryCollection } from '@nuxt/content/server'
|
||||
|
||||
export default defineCachedEventHandler(async (event) => {
|
||||
const result = await queryCollection(event, 'projects')
|
||||
.where('extension', '=', 'md')
|
||||
.all()
|
||||
|
||||
if (result.length === 0) {
|
||||
throw createError({ statusCode: 404, statusMessage: 'Projects not found' })
|
||||
}
|
||||
|
||||
return result
|
||||
}, {
|
||||
maxAge: 60 * 60 * 24,
|
||||
name: 'projects'
|
||||
})
|
||||
16
server/api/skills.get.ts
Normal file
16
server/api/skills.get.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { queryCollection } from '@nuxt/content/server'
|
||||
|
||||
export default defineCachedEventHandler(async (event) => {
|
||||
const result = await queryCollection(event, 'skills')
|
||||
.where('extension', '=', 'json')
|
||||
.first()
|
||||
|
||||
if (!result) {
|
||||
throw createError({ statusCode: 404, statusMessage: 'Skills not found' })
|
||||
}
|
||||
|
||||
return result.body
|
||||
}, {
|
||||
maxAge: 60 * 60 * 24,
|
||||
name: 'skills'
|
||||
})
|
||||
57
server/api/stats.get.ts
Normal file
57
server/api/stats.get.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import type { H3Event } from 'h3'
|
||||
|
||||
const cachedWakatimeCoding = defineCachedFunction(async (event: H3Event) => {
|
||||
const config = useRuntimeConfig(event)
|
||||
|
||||
return await $fetch<{ data: unknown[] }>(`https://wakatime.com/share/${config.wakatime.userId}/${config.wakatime.coding}.json`)
|
||||
}, {
|
||||
maxAge: 24 * 60 * 60,
|
||||
name: 'wakatime',
|
||||
getKey: () => 'coding'
|
||||
})
|
||||
|
||||
const cachedWakatimeEditors = defineCachedFunction(async (event: H3Event) => {
|
||||
const config = useRuntimeConfig(event)
|
||||
|
||||
return await $fetch<{ data: unknown[] }>(`https://wakatime.com/share/${config.wakatime.userId}/${config.wakatime.editors}.json`)
|
||||
}, {
|
||||
maxAge: 24 * 60 * 60,
|
||||
name: 'wakatime',
|
||||
getKey: () => 'editors'
|
||||
})
|
||||
|
||||
const cachedWakatimeOs = defineCachedFunction(async (event: H3Event) => {
|
||||
const config = useRuntimeConfig(event)
|
||||
|
||||
return await $fetch<{ data: unknown[] }>(`https://wakatime.com/share/${config.wakatime.userId}/${config.wakatime.os}.json`)
|
||||
}, {
|
||||
maxAge: 24 * 60 * 60,
|
||||
name: 'wakatime',
|
||||
getKey: () => 'os'
|
||||
})
|
||||
|
||||
const cachedWakatimeLanguages = defineCachedFunction(async (event: H3Event) => {
|
||||
const config = useRuntimeConfig(event)
|
||||
|
||||
return await $fetch<{ data: unknown[] }>(`https://wakatime.com/share/${config.wakatime.userId}/${config.wakatime.languages}.json`)
|
||||
}, {
|
||||
maxAge: 24 * 60 * 60,
|
||||
name: 'wakatime',
|
||||
getKey: () => 'languages'
|
||||
})
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const [coding, editors, os, languages] = await Promise.all([
|
||||
cachedWakatimeCoding(event),
|
||||
cachedWakatimeEditors(event),
|
||||
cachedWakatimeOs(event),
|
||||
cachedWakatimeLanguages(event)
|
||||
])
|
||||
|
||||
return {
|
||||
coding: coding.data,
|
||||
editors: editors.data,
|
||||
os: os.data,
|
||||
languages: languages.data
|
||||
}
|
||||
})
|
||||
7
server/api/status-page.get.ts
Normal file
7
server/api/status-page.get.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export default defineCachedEventHandler(async (event) => {
|
||||
const { statusPage } = useRuntimeConfig(event)
|
||||
return await $fetch(statusPage)
|
||||
}, {
|
||||
maxAge: 60 * 60,
|
||||
name: 'status-page'
|
||||
})
|
||||
33
server/api/uses.get.ts
Normal file
33
server/api/uses.get.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { queryCollection } from '@nuxt/content/server'
|
||||
|
||||
export default defineCachedEventHandler(async (event) => {
|
||||
const categories = await queryCollection(event, 'usesCategories')
|
||||
.where('extension', '=', 'md')
|
||||
.all()
|
||||
|
||||
if (categories.length === 0) {
|
||||
throw createError({ statusCode: 404, statusMessage: 'Uses categories not found' })
|
||||
}
|
||||
|
||||
const uses = await queryCollection(event, 'uses')
|
||||
.where('extension', '=', 'md')
|
||||
.all()
|
||||
|
||||
if (uses.length === 0) {
|
||||
throw createError({ statusCode: 404, statusMessage: 'Uses not found' })
|
||||
}
|
||||
|
||||
const uses_by_categories = []
|
||||
for (const category of categories) {
|
||||
uses_by_categories.push({
|
||||
category: category,
|
||||
uses: uses.filter((use: { category: unknown }) => use.category === category.slug)
|
||||
})
|
||||
}
|
||||
|
||||
return uses_by_categories
|
||||
},
|
||||
{
|
||||
maxAge: 60 * 60 * 24,
|
||||
name: 'uses'
|
||||
})
|
||||
Reference in New Issue
Block a user