From 8b69a123425a93450738e530847c781b7ab5f5ed Mon Sep 17 00:00:00 2001 From: kayjaydee Date: Fri, 10 Apr 2026 19:19:36 +0200 Subject: [PATCH] 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. --- Dockerfile | 20 +++++++-- app/data/site.ts | 6 +-- package.json | 4 +- public/sitemap.xml | 86 ------------------------------------ server/plugins/rate-limit.ts | 32 ++++++++++++++ 5 files changed, 53 insertions(+), 95 deletions(-) delete mode 100644 public/sitemap.xml create mode 100644 server/plugins/rate-limit.ts diff --git a/Dockerfile b/Dockerfile index 725215d..6470778 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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"] diff --git a/app/data/site.ts b/app/data/site.ts index e08c30e..0ffe12b 100644 --- a/app/data/site.ts +++ b/app/data/site.ts @@ -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', }, }, }, diff --git a/package.json b/package.json index 40cc97f..aab7b55 100644 --- a/package.json +++ b/package.json @@ -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": { diff --git a/public/sitemap.xml b/public/sitemap.xml deleted file mode 100644 index 89a5864..0000000 --- a/public/sitemap.xml +++ /dev/null @@ -1,86 +0,0 @@ - - - - - - https://killiandalcin.fr/ - 2025-07-07 - - - - - https://killiandalcin.fr/fiverr - 2025-07-07 - - - - - https://killiandalcin.fr/projects - 2025-07-07 - - - - - https://killiandalcin.fr/contact - 2025-07-07 - - - - - https://killiandalcin.fr/about - 2025-07-07 - - - - - https://killiandalcin.fr/formation - 2025-07-07 - - - - - - - https://killiandalcin.fr/project/virtual-tour - 2025-07-07 - - - - - https://killiandalcin.fr/project/xinko - 2025-07-07 - - - - - https://killiandalcin.fr/project/image-manipulation - 2025-07-07 - - - - - https://killiandalcin.fr/project/flowboard - 2025-07-07 - - - - - https://killiandalcin.fr/project/primate-web-admin - 2025-07-07 - - - - - https://killiandalcin.fr/project/instagram-bot - 2025-07-07 - - - - - https://killiandalcin.fr/project/crowdin-status-bot - 2025-07-07 - - - \ No newline at end of file diff --git a/server/plugins/rate-limit.ts b/server/plugins/rate-limit.ts new file mode 100644 index 0000000..7ee6819 --- /dev/null +++ b/server/plugins/rate-limit.ts @@ -0,0 +1,32 @@ +const ipMap = new Map() + +// 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.' }) + } + }) +})