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:
+16
-4
@@ -1,17 +1,29 @@
|
||||
# Stage 1: Build
|
||||
FROM node:22-alpine AS builder
|
||||
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 . .
|
||||
RUN npm run build
|
||||
RUN pnpm build
|
||||
|
||||
# Stage 2: Runtime
|
||||
FROM node:22-alpine AS runner
|
||||
WORKDIR /app
|
||||
ENV NODE_ENV=production
|
||||
ENV HOST=0.0.0.0
|
||||
ENV PORT=3000
|
||||
WORKDIR /app
|
||||
|
||||
# Nuxt SSR bundles all server deps into .output/server/
|
||||
COPY --from=builder /app/.output /app/.output
|
||||
|
||||
EXPOSE 3000
|
||||
CMD ["node", "/app/.output/server/index.mjs"]
|
||||
|
||||
+3
-3
@@ -58,13 +58,13 @@ export const siteConfig: SiteConfig = {
|
||||
},
|
||||
{
|
||||
id: 'telegram-bot',
|
||||
url: '#',
|
||||
url: 'https://www.fiverr.com/users/mr_kayjaydee',
|
||||
image: '/images/fiverr/telegram_bot.webp',
|
||||
price: '$20',
|
||||
},
|
||||
{
|
||||
id: 'website-development',
|
||||
url: '#',
|
||||
url: 'https://www.fiverr.com/users/mr_kayjaydee',
|
||||
image: '/images/fiverr/website.webp',
|
||||
price: '$50',
|
||||
},
|
||||
@@ -96,7 +96,7 @@ export const siteConfig: SiteConfig = {
|
||||
priceRange: '$$$',
|
||||
aggregateRating: {
|
||||
ratingValue: '5',
|
||||
reviewCount: '50',
|
||||
reviewCount: '10',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
+2
-2
@@ -21,8 +21,8 @@
|
||||
"nodemailer": "^8.0.5",
|
||||
"nuxt": "^4.0.0",
|
||||
"nuxt-gtag": "^4.1.0",
|
||||
"vue": "latest",
|
||||
"vue-router": "latest",
|
||||
"vue": "^3.5.0",
|
||||
"vue-router": "^4.5.0",
|
||||
"zod": "^4.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -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>
|
||||
@@ -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.' })
|
||||
}
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user