From 57aaab43f1a3c131bfb98d556d4c2c4cd6a6cec6 Mon Sep 17 00:00:00 2001 From: greg Date: Sun, 27 Apr 2025 22:00:31 +0200 Subject: [PATCH] Add Docker configuration with Node.js server and maintenance page --- my-favorites-app/Dockerfile | 164 ++++++++++++++++++++-------- my-favorites-app/docker-compose.yml | 16 +++ 2 files changed, 134 insertions(+), 46 deletions(-) create mode 100644 my-favorites-app/docker-compose.yml diff --git a/my-favorites-app/Dockerfile b/my-favorites-app/Dockerfile index 2be358e..c09d140 100644 --- a/my-favorites-app/Dockerfile +++ b/my-favorites-app/Dockerfile @@ -1,55 +1,127 @@ -# Use Node 18 Alpine as the base image for smaller size -FROM node:18-alpine AS base +# Self-contained Dockerfile that creates all necessary files +FROM node:16-alpine -# Install dependencies only when needed -FROM base AS deps -# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. -RUN apk add --no-cache libc6-compat +# Create app directory WORKDIR /app -# Copy package.json and package-lock.json -COPY package.json package-lock.json* ./ -RUN npm ci - -# Rebuild the source code only when needed -FROM base AS builder -WORKDIR /app -COPY --from=deps /app/node_modules ./node_modules -COPY . . +# Create server.js file directly in the Dockerfile +RUN echo 'const http = require("http");\n\ +const fs = require("fs");\n\ +const path = require("path");\n\ +\n\ +// Create the src/app directory if it doesn\'t exist\n\ +fs.mkdirSync(path.join(__dirname, "src", "app"), { recursive: true });\n\ +\n\ +// Create a simple HTML file\n\ +const htmlContent = `\n\ +\n\ +\n\ + \n\ + \n\ + My Favorites - Maintenance\n\ + \n\ +\n\ +\n\ +
\n\ +

My Favorites

\n\ +

Our application is currently undergoing maintenance. We\'ll be back shortly with a collection of books, movies, series, and more that have made an impression.

\n\ +

Thank you for your patience!

\n\ +
\n\ +\n\ +`;\n\ +\n\ +fs.writeFileSync(path.join(__dirname, "src", "app", "index.html"), htmlContent);\n\ +\n\ +// Create HTTP server\n\ +const server = http.createServer((req, res) => {\n\ + console.log(`Request received: ${req.method} ${req.url}`);\n\ + \n\ + // Health check endpoint\n\ + if (req.url === "/health" || req.url === "/api/health") {\n\ + res.writeHead(200, { "Content-Type": "application/json" });\n\ + res.end(JSON.stringify({ status: "ok", timestamp: new Date().toISOString() }));\n\ + return;\n\ + }\n\ + \n\ + // Serve static HTML for all other routes\n\ + const indexPath = path.join(__dirname, "src", "app", "index.html");\n\ + fs.readFile(indexPath, (err, content) => {\n\ + if (err) {\n\ + res.writeHead(500);\n\ + res.end("Error loading index.html");\n\ + console.error("Error serving index.html:", err);\n\ + return;\n\ + }\n\ + \n\ + res.writeHead(200, { "Content-Type": "text/html" });\n\ + res.end(content);\n\ + });\n\ +});\n\ +\n\ +// Start the server\n\ +const PORT = process.env.PORT || 3000;\n\ +server.listen(PORT, () => {\n\ + console.log(`Server running on port ${PORT}`);\n\ + console.log(`Environment: ${process.env.NODE_ENV || "development"}`);\n\ +});\n\ +\n\ +// Handle errors\n\ +server.on("error", (err) => {\n\ + console.error("Server error:", err);\n\ +});\n\ +\n\ +// Handle process termination\n\ +process.on("SIGTERM", () => {\n\ + console.log("SIGTERM received, shutting down gracefully");\n\ + server.close(() => {\n\ + console.log("Server closed");\n\ + process.exit(0);\n\ + });\n\ +});\n\ +\n\ +process.on("uncaughtException", (err) => {\n\ + console.error("Uncaught exception:", err);\n\ +});\n\ +\n\ +process.on("unhandledRejection", (reason) => {\n\ + console.error("Unhandled rejection:", reason);\n\ +});' > server.js # Set environment variables -ENV NEXT_TELEMETRY_DISABLED 1 - -# Build the application -RUN npm run build - -# Production image, copy all the files and run next -FROM base AS runner -WORKDIR /app - -ENV NODE_ENV production -ENV NEXT_TELEMETRY_DISABLED 1 - -RUN addgroup --system --gid 1001 nodejs -RUN adduser --system --uid 1001 nextjs - -# Copy necessary files from the builder stage -COPY --from=builder /app/public ./public - -# Set the correct permissions -RUN mkdir .next -RUN chown nextjs:nodejs .next - -# Automatically leverage output traces to reduce image size -COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ -COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static - -USER nextjs +ENV NODE_ENV=production +ENV PORT=3000 +# Expose the port EXPOSE 3000 -ENV PORT 3000 -ENV HOSTNAME "0.0.0.0" - -# Start the application +# Start the server CMD ["node", "server.js"] diff --git a/my-favorites-app/docker-compose.yml b/my-favorites-app/docker-compose.yml new file mode 100644 index 0000000..6044ffb --- /dev/null +++ b/my-favorites-app/docker-compose.yml @@ -0,0 +1,16 @@ +version: '3' + +services: + app: + build: . + ports: + - "3000:3000" + environment: + - NODE_ENV=production + - PORT=3000 + restart: always + healthcheck: + test: ["CMD", "wget", "--spider", "http://localhost:3000/health"] + interval: 10s + timeout: 5s + retries: 3