From c7b022d5024def84298a76c521588aca0ee5f184 Mon Sep 17 00:00:00 2001 From: Moh Dzulfikri Maulana <106526316+Dzuuul@users.noreply.github.com> Date: Mon, 9 Mar 2026 00:13:31 +0700 Subject: [PATCH] feat: Containerize the application with Docker and Docker Compose, including a PostgreSQL database and a deployment script. --- .gitignore | 12 +++++++++- .vscode/settings.json | 3 +++ Dockerfile | 55 +++++++++++++++++++++++++++++++++++++++++++ deploy.sh | 23 ++++++++++++++++++ docker-compose.yml | 46 ++++++++++++++++++++++++++++++++++++ next.config.ts | 1 + 6 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 .vscode/settings.json create mode 100644 Dockerfile create mode 100755 deploy.sh create mode 100644 docker-compose.yml diff --git a/.gitignore b/.gitignore index d9ee72c..d66a26f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,13 @@ node_modules .next -.env \ No newline at end of file +.env + +.DS_Store +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +dist +build diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..6b0e5ab --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "postman.settings.dotenv-detection-notification-visibility": false +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..98e348d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,55 @@ +# Stage 1: Install dependencies +FROM node:20-alpine AS deps +RUN apk add --no-cache libc6-compat +WORKDIR /app + +# Install pnpm +RUN npm install -g pnpm + +# Copy package files +COPY package.json pnpm-lock.yaml ./ +# Copy prisma directory to generate client +COPY prisma ./prisma/ + +# Install dependencies +RUN pnpm install --frozen-lockfile + +# Stage 2: Build the app +FROM node:20-alpine AS builder +WORKDIR /app +RUN npm install -g pnpm +COPY --from=deps /app/node_modules ./node_modules +COPY . . + +# Generate Prisma Client +RUN pnpm prisma generate + +# Build Next.js +# Disable telemetry during build +ENV NEXT_TELEMETRY_DISABLED 1 +RUN pnpm build + +# Stage 3: Runner +FROM node:20-alpine 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 +COPY --from=builder /app/public ./public +COPY --from=builder /app/prisma ./prisma +COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static + +USER nextjs + +EXPOSE 4000 + +ENV PORT 4000 +ENV HOSTNAME "0.0.0.0" + +CMD ["node", "server.js"] diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 0000000..7dbc9bc --- /dev/null +++ b/deploy.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +# Load environment variables from .env if it exists +if [ -f .env ]; then + export $(grep -v '^#' .env | xargs) +fi + +# Name of the database container +DB_CONTAINER_NAME="portfolio-db" + +# Check if the database container is already running +if [ "$(docker ps -q -f name=${DB_CONTAINER_NAME})" ]; then + echo "✅ Database container '${DB_CONTAINER_NAME}' is already running." + echo "🚀 Deploying/Updating only the application..." + docker-compose up -d --no-recreate db + docker-compose up -d app +else + echo "⚠️ Database container '${DB_CONTAINER_NAME}' not found or stopped." + echo "🏗️ Deploying full stack (Database + App)..." + docker-compose up -d +fi + +echo "✨ Deployment script finished!" diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..e708607 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,46 @@ +version: '3.8' + +services: + db: + image: postgres:15-alpine + container_name: portfolio-db + restart: always + environment: + POSTGRES_USER: ${POSTGRES_USER:-fikri} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-fikri} + POSTGRES_DB: ${POSTGRES_DB:-fullstack-portfolio} + ports: + - "5429:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-fikri} -d ${POSTGRES_DB:-fullstack-portfolio}"] + interval: 5s + timeout: 5s + retries: 5 + + app: + build: + context: . + dockerfile: Dockerfile + container_name: portfolio-app + restart: always + ports: + - "4000:4000" + environment: + DATABASE_URL: postgresql://${POSTGRES_USER:-fikri}:${POSTGRES_PASSWORD:-fikri}@db:5432/${POSTGRES_DB:-fullstack-portfolio}?schema=public + JWT_SECRET: ${JWT_SECRET} + R2_TOKEN: ${R2_TOKEN} + R2_ACCESS_KEY: ${R2_ACCESS_KEY} + R2_SECRET_KEY: ${R2_SECRET_KEY} + R2_ENDPOINT: ${R2_ENDPOINT} + R2_BUCKET_NAME: ${R2_BUCKET_NAME} + IMAGE_URL: ${IMAGE_URL} + depends_on: + db: + condition: service_healthy + command: > + sh -c "npx prisma db push && node server.js" + +volumes: + postgres_data: diff --git a/next.config.ts b/next.config.ts index fa67b35..e44f3f5 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,6 +1,7 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { + output: 'standalone', turbopack: { root: __dirname, },