From 9abf68320a6acd9a8570343114a9d205c8166e7e Mon Sep 17 00:00:00 2001 From: Moh Dzulfikri Maulana <106526316+Dzuuul@users.noreply.github.com> Date: Mon, 9 Mar 2026 01:11:58 +0700 Subject: [PATCH] feat: Initialize Prisma database schema and update deployment scripts for robust migration handling. --- Dockerfile | 4 +- deploy.sh | 44 ++++-- docker-compose.yml | 4 +- .../20260308180502_init/migration.sql | 143 ++++++++++++++++++ prisma/migrations/migration_lock.toml | 3 + .../dashboard/cms/analytics/track/route.ts | 4 +- 6 files changed, 183 insertions(+), 19 deletions(-) create mode 100644 prisma/migrations/20260308180502_init/migration.sql create mode 100644 prisma/migrations/migration_lock.toml diff --git a/Dockerfile b/Dockerfile index 98e348d..5b5432b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,13 +16,14 @@ RUN pnpm install --frozen-lockfile # Stage 2: Build the app FROM node:20-alpine AS builder +RUN apk add --no-cache libc6-compat WORKDIR /app RUN npm install -g pnpm COPY --from=deps /app/node_modules ./node_modules COPY . . # Generate Prisma Client -RUN pnpm prisma generate +RUN DATABASE_URL="postgresql://user:password@localhost:5432/db" pnpm prisma generate # Build Next.js # Disable telemetry during build @@ -31,6 +32,7 @@ RUN pnpm build # Stage 3: Runner FROM node:20-alpine AS runner +RUN apk add --no-cache libc6-compat WORKDIR /app ENV NODE_ENV production diff --git a/deploy.sh b/deploy.sh index f54f448..6c9fd3f 100755 --- a/deploy.sh +++ b/deploy.sh @@ -1,7 +1,13 @@ #!/bin/bash +# Exit immediately if a command exits with a non-zero status +set -e + +echo "Starting Production Deployment..." + # Load environment variables from .env if it exists if [ -f .env ]; then + echo "Loading environment variables from .env..." export $(grep -v '^#' .env | xargs) fi @@ -11,23 +17,35 @@ if docker compose version >/dev/null 2>&1; then elif docker-compose version >/dev/null 2>&1; then COMPOSE_CMD="docker-compose" else - echo "❌ Error: Docker Compose not found. Please install Docker Compose first." + echo "Error: Docker Compose not found. Please install Docker Compose first." exit 1 fi -# Name of the database container -DB_CONTAINER_NAME="portfolio-db" +# Configuration +DB_CONTAINER_NAME="fikri-portfolio-db" +DB_PORT="5429" -# 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..." - $COMPOSE_CMD up -d --no-recreate db - $COMPOSE_CMD up -d app +# Check if any container is using the DB port or if our specific container is running +EXISTING_DB=$(docker ps -q -f publish=${DB_PORT}) + +if [ ! -z "$EXISTING_DB" ]; then + echo "Database is already running on port ${DB_PORT}. Container ID: $EXISTING_DB" + echo "Building and updating only the application..." + # --build ensures we use the latest code + # --no-deps ensures we don't restart the DB if it's healthy + $COMPOSE_CMD up -d --build --no-deps app else - echo "⚠️ Database container '${DB_CONTAINER_NAME}' not found or stopped." - echo "🏗️ Deploying full stack (Database + App)..." - $COMPOSE_CMD up -d + echo "Database not found on port ${DB_PORT}." + echo "Full deployment (Database + App)..." + $COMPOSE_CMD up -d --build fi -echo "✨ Deployment script finished!" +# Health check - optional but recommended +echo "Waiting for application to be healthy..." +# You could add a curl check here if desired + +# Cleanup: remove dangling images to save disk space +echo "Cleaning up old images..." +docker image prune -f + +echo "Deployment script finished successfully!" diff --git a/docker-compose.yml b/docker-compose.yml index e708607..e6a8964 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,3 @@ -version: '3.8' - services: db: image: postgres:15-alpine @@ -40,7 +38,7 @@ services: db: condition: service_healthy command: > - sh -c "npx prisma db push && node server.js" + sh -c "npx prisma migrate deploy && node server.js" volumes: postgres_data: diff --git a/prisma/migrations/20260308180502_init/migration.sql b/prisma/migrations/20260308180502_init/migration.sql new file mode 100644 index 0000000..8bbb724 --- /dev/null +++ b/prisma/migrations/20260308180502_init/migration.sql @@ -0,0 +1,143 @@ +-- CreateEnum +CREATE TYPE "BlogStatus" AS ENUM ('DRAFT', 'SCHEDULED', 'PUBLISHED'); + +-- CreateTable +CREATE TABLE "User" ( + "id" TEXT NOT NULL, + "email" TEXT NOT NULL, + "name" TEXT NOT NULL, + "password" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "User_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Session" ( + "id" TEXT NOT NULL, + "visitorId" TEXT NOT NULL, + "startedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "lastSeenAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Session_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "PageView" ( + "id" TEXT NOT NULL, + "sessionId" TEXT NOT NULL, + "path" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "PageView_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Project" ( + "id" TEXT NOT NULL, + "title" TEXT NOT NULL, + "description" TEXT NOT NULL, + "imageUrl" TEXT, + "liveUrl" TEXT, + "githubUrl" TEXT, + "techStack" TEXT[], + "featured" BOOLEAN NOT NULL DEFAULT false, + "order" INTEGER NOT NULL DEFAULT 0, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Project_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Experience" ( + "id" TEXT NOT NULL, + "company" TEXT NOT NULL, + "role" TEXT NOT NULL, + "startDate" TIMESTAMP(3) NOT NULL, + "endDate" TIMESTAMP(3), + "current" BOOLEAN NOT NULL DEFAULT false, + "description" TEXT NOT NULL, + "highlights" TEXT[], + "techStack" TEXT[], + "order" INTEGER NOT NULL DEFAULT 0, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Experience_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "ContactMessage" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + "email" TEXT NOT NULL, + "subject" TEXT NOT NULL, + "message" TEXT NOT NULL, + "read" BOOLEAN NOT NULL DEFAULT false, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "ContactMessage_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Blog" ( + "id" TEXT NOT NULL, + "title" TEXT NOT NULL, + "slug" TEXT NOT NULL, + "content" TEXT NOT NULL, + "excerpt" TEXT, + "status" "BlogStatus" NOT NULL DEFAULT 'DRAFT', + "scheduledAt" TIMESTAMP(3), + "publishedAt" TIMESTAMP(3), + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Blog_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Tag" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Tag_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "_BlogToTag" ( + "A" TEXT NOT NULL, + "B" TEXT NOT NULL, + + CONSTRAINT "_BlogToTag_AB_pkey" PRIMARY KEY ("A","B") +); + +-- CreateIndex +CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); + +-- CreateIndex +CREATE UNIQUE INDEX "Blog_slug_key" ON "Blog"("slug"); + +-- CreateIndex +CREATE INDEX "Blog_status_idx" ON "Blog"("status"); + +-- CreateIndex +CREATE INDEX "Blog_scheduledAt_idx" ON "Blog"("scheduledAt"); + +-- CreateIndex +CREATE UNIQUE INDEX "Tag_name_key" ON "Tag"("name"); + +-- CreateIndex +CREATE INDEX "_BlogToTag_B_index" ON "_BlogToTag"("B"); + +-- AddForeignKey +ALTER TABLE "PageView" ADD CONSTRAINT "PageView_sessionId_fkey" FOREIGN KEY ("sessionId") REFERENCES "Session"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "_BlogToTag" ADD CONSTRAINT "_BlogToTag_A_fkey" FOREIGN KEY ("A") REFERENCES "Blog"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "_BlogToTag" ADD CONSTRAINT "_BlogToTag_B_fkey" FOREIGN KEY ("B") REFERENCES "Tag"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml new file mode 100644 index 0000000..044d57c --- /dev/null +++ b/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (e.g., Git) +provider = "postgresql" diff --git a/src/app/api/dashboard/cms/analytics/track/route.ts b/src/app/api/dashboard/cms/analytics/track/route.ts index 6e16f88..352f521 100644 --- a/src/app/api/dashboard/cms/analytics/track/route.ts +++ b/src/app/api/dashboard/cms/analytics/track/route.ts @@ -1,11 +1,11 @@ import { prisma } from "@/lib/prisma"; export async function POST(req: Request) { - const { visitorId, path } = await req.json(); + const { visitorId: sessionId, path } = await req.json(); await prisma.pageView.create({ data: { - visitorId, + sessionId, path, }, });