chore: update Dockerfile for pnpm, modify package.json dependencies, and implement rate limiting

- Switched from npm to pnpm for dependency management in Dockerfile, improving build efficiency.
- Updated Vue and Vue Router versions in package.json for better compatibility.
- Changed placeholder URLs in site.ts to actual Fiverr links and adjusted review count.
- Removed obsolete sitemap.xml file to streamline the project.
- Added a new rate limiting plugin to manage API request limits for the contact endpoint.
This commit is contained in:
2026-04-10 19:19:36 +02:00
parent c859a9feb8
commit ca3b60b0ad
5 changed files with 53 additions and 95 deletions
+16 -4
View File
@@ -1,17 +1,29 @@
# Stage 1: Build # Stage 1: Build
FROM node:22-alpine AS builder FROM node:22-alpine AS builder
WORKDIR /app WORKDIR /app
COPY package*.json ./
RUN npm ci # Install pnpm via corepack
RUN corepack enable && corepack prepare pnpm@latest --activate
# Copy manifests first for layer caching
COPY package.json pnpm-lock.yaml ./
# Install all dependencies (including devDeps needed for build)
RUN pnpm install --frozen-lockfile
# Copy source and build
COPY . . COPY . .
RUN npm run build RUN pnpm build
# Stage 2: Runtime # Stage 2: Runtime
FROM node:22-alpine AS runner FROM node:22-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production ENV NODE_ENV=production
ENV HOST=0.0.0.0 ENV HOST=0.0.0.0
ENV PORT=3000 ENV PORT=3000
WORKDIR /app
# Nuxt SSR bundles all server deps into .output/server/
COPY --from=builder /app/.output /app/.output COPY --from=builder /app/.output /app/.output
EXPOSE 3000 EXPOSE 3000
CMD ["node", "/app/.output/server/index.mjs"] CMD ["node", "/app/.output/server/index.mjs"]
+3 -3
View File
@@ -58,13 +58,13 @@ export const siteConfig: SiteConfig = {
}, },
{ {
id: 'telegram-bot', id: 'telegram-bot',
url: '#', url: 'https://www.fiverr.com/users/mr_kayjaydee',
image: '/images/fiverr/telegram_bot.webp', image: '/images/fiverr/telegram_bot.webp',
price: '$20', price: '$20',
}, },
{ {
id: 'website-development', id: 'website-development',
url: '#', url: 'https://www.fiverr.com/users/mr_kayjaydee',
image: '/images/fiverr/website.webp', image: '/images/fiverr/website.webp',
price: '$50', price: '$50',
}, },
@@ -96,7 +96,7 @@ export const siteConfig: SiteConfig = {
priceRange: '$$$', priceRange: '$$$',
aggregateRating: { aggregateRating: {
ratingValue: '5', ratingValue: '5',
reviewCount: '50', reviewCount: '10',
}, },
}, },
}, },
+2 -2
View File
@@ -21,8 +21,8 @@
"nodemailer": "^8.0.5", "nodemailer": "^8.0.5",
"nuxt": "^4.0.0", "nuxt": "^4.0.0",
"nuxt-gtag": "^4.1.0", "nuxt-gtag": "^4.1.0",
"vue": "latest", "vue": "^3.5.0",
"vue-router": "latest", "vue-router": "^4.5.0",
"zod": "^4.3.6" "zod": "^4.3.6"
}, },
"devDependencies": { "devDependencies": {
-86
View File
@@ -1,86 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
<!-- Home Page -->
<url>
<loc>https://killiandalcin.fr/</loc>
<lastmod>2025-07-07</lastmod>
</url>
<!-- Fiverr Services Page -->
<url>
<loc>https://killiandalcin.fr/fiverr</loc>
<lastmod>2025-07-07</lastmod>
</url>
<!-- Projects Portfolio Page -->
<url>
<loc>https://killiandalcin.fr/projects</loc>
<lastmod>2025-07-07</lastmod>
</url>
<!-- Contact Page -->
<url>
<loc>https://killiandalcin.fr/contact</loc>
<lastmod>2025-07-07</lastmod>
</url>
<!-- About Page -->
<url>
<loc>https://killiandalcin.fr/about</loc>
<lastmod>2025-07-07</lastmod>
</url>
<!-- Formation/Training Page -->
<url>
<loc>https://killiandalcin.fr/formation</loc>
<lastmod>2025-07-07</lastmod>
</url>
<!-- Dynamic Project Detail Pages -->
<!-- Virtual Tour Project -->
<url>
<loc>https://killiandalcin.fr/project/virtual-tour</loc>
<lastmod>2025-07-07</lastmod>
</url>
<!-- Xinko Discord Bot Project -->
<url>
<loc>https://killiandalcin.fr/project/xinko</loc>
<lastmod>2025-07-07</lastmod>
</url>
<!-- Image Manipulation NPM Package -->
<url>
<loc>https://killiandalcin.fr/project/image-manipulation</loc>
<lastmod>2025-07-07</lastmod>
</url>
<!-- Flowboard Project Management App -->
<url>
<loc>https://killiandalcin.fr/project/flowboard</loc>
<lastmod>2025-07-07</lastmod>
</url>
<!-- Primate Web Admin Project -->
<url>
<loc>https://killiandalcin.fr/project/primate-web-admin</loc>
<lastmod>2025-07-07</lastmod>
</url>
<!-- Instagram Bot Project -->
<url>
<loc>https://killiandalcin.fr/project/instagram-bot</loc>
<lastmod>2025-07-07</lastmod>
</url>
<!-- Crowdin Status Bot Project -->
<url>
<loc>https://killiandalcin.fr/project/crowdin-status-bot</loc>
<lastmod>2025-07-07</lastmod>
</url>
</urlset>
+32
View File
@@ -0,0 +1,32 @@
const ipMap = new Map<string, { count: number; reset: number }>()
// Clean stale entries every 5 minutes to prevent memory leak
setInterval(() => {
const now = Date.now()
for (const [ip, entry] of ipMap) {
if (entry.reset < now) ipMap.delete(ip)
}
}, 5 * 60 * 1000)
export default defineNitroPlugin((nitro) => {
nitro.hooks.hook('request', (event) => {
// Only rate-limit the contact POST endpoint
if (event.method !== 'POST' || !event.path.startsWith('/api/contact')) return
const ip = getRequestIP(event, { xForwardedFor: true }) ?? 'unknown'
const now = Date.now()
const window = 60_000 // 1 minute window
const limit = 3 // max 3 requests per minute per IP
const entry = ipMap.get(ip)
if (!entry || entry.reset < now) {
ipMap.set(ip, { count: 1, reset: now + window })
return
}
entry.count++
if (entry.count > limit) {
throw createError({ statusCode: 429, message: 'Too many requests. Please try again later.' })
}
})
})