feat: Initialize Prisma database schema and update deployment scripts for robust migration handling.

This commit is contained in:
Moh Dzulfikri Maulana
2026-03-09 01:11:58 +07:00
parent ecd09904cf
commit 9abf68320a
6 changed files with 183 additions and 19 deletions

View File

@@ -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

View File

@@ -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!"

View File

@@ -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:

View File

@@ -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;

View File

@@ -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"

View File

@@ -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,
},
});