diff --git a/README.md b/README.md index 0749db2..830b900 100644 --- a/README.md +++ b/README.md @@ -1,112 +1,100 @@ -# Portfolio — Senior Fullstack Engineer +# Portfolio — Fullstack Developer -A modern, production-grade portfolio website built with Next.js 15, TailwindCSS, Framer Motion, and Prisma. +A modern, production-grade portfolio website built with Next.js 16, TailwindCSS, Framer Motion, and Prisma. ## Tech Stack -- **Frontend**: Next.js 15 (App Router), React 19, TypeScript +- **Frontend**: Next.js 16 (App Router), React 19, TypeScript - **Styling**: TailwindCSS + custom design tokens - **Animations**: Framer Motion - **ORM**: Prisma (PostgreSQL) - **Validation**: Zod + React Hook Form - **Icons**: Lucide React + React Icons +- **Infrastructure**: Docker + Docker Compose ## Project Structure ``` src/ ├── app/ -│ ├── api/ -│ │ └── contact/ # Contact form API route -│ ├── globals.css +│ ├── api/ # API Routes (Analytics, Contact, etc.) +│ ├── blog/ # Blog pages +│ ├── dashboard/ # Admin dashboard │ ├── layout.tsx # Root layout with SEO metadata │ └── page.tsx # Home page (Server Component) ├── components/ │ ├── sections/ # Full-page section components -│ │ ├── Navbar.tsx -│ │ ├── HeroSection.tsx -│ │ ├── AboutSection.tsx -│ │ ├── TechStackSection.tsx -│ │ ├── ProjectsSection.tsx -│ │ ├── ExperienceSection.tsx -│ │ ├── ArchitectureSection.tsx -│ │ ├── ContactSection.tsx -│ │ └── Footer.tsx -│ └── ui/ # Reusable UI components -│ └── ProjectCard.tsx +│ └── ui/ # Reusable UI components (shadcn/ui style) ├── lib/ │ ├── prisma.ts # Prisma client singleton -│ └── utils.ts # cn() utility -└── types/ - └── index.ts # TypeScript types -prisma/ -├── schema.prisma # Database schema -└── seed.ts # Seed data +│ ├── s3.ts # Storage configuration (Cloudflare R2) +│ └── visitor.ts # Analytics tracking logic +├── prisma/ +│ ├── schema.prisma # Database schema +│ └── migrations/ # Database migration history +├── Dockerfile # Multi-stage production build +├── docker-compose.yml # Infrastructure orchestration +└── deploy.sh # Production deployment script ``` ## Getting Started ### 1. Install dependencies ```bash -npm install -# Also install react-icons for tech stack icons -npm install react-icons +pnpm install ``` ### 2. Setup environment ```bash cp .env.example .env -# Edit .env with your DATABASE_URL +# Edit .env with your DATABASE_URL and other credentials ``` ### 3. Setup database ```bash -npm run db:generate # Generate Prisma client -npm run db:push # Push schema to database -npx ts-node prisma/seed.ts # Seed with sample data +pnpm db:generate # Generate Prisma client +npx prisma migrate dev # Run migrations ``` ### 4. Run development server ```bash -npm run dev +pnpm dev ``` Open [http://localhost:3000](http://localhost:3000) -## Customization +## Deployment (Production Ready) -### Personal Information -Update these files with your information: -- `src/app/layout.tsx` — SEO metadata (name, description, URL) -- `src/components/sections/HeroSection.tsx` — Hero text & social links -- `src/components/sections/AboutSection.tsx` — Bio, stats, skills -- `src/components/sections/Footer.tsx` — Social links, email -- `src/components/sections/ContactSection.tsx` — Contact info +Project ini sudah dilengkapi dengan konfigurasi Docker untuk deployment mandiri (self-hosted). -### Database Content -After running seed, use Prisma Studio to manage content: +### Deploy menggunakan Docker +1. Pastikan Docker dan Docker Compose sudah terinstall di server. +2. Siapkan file `.env` di root direktori. +3. Jalankan script deployment: ```bash -npm run db:studio +chmod +x deploy.sh +./deploy.sh ``` -Or update `prisma/seed.ts` with your actual projects and experience data. +Script `deploy.sh` akan secara otomatis: +- Mendeteksi apakah database sudah berjalan (mencegah konflik). +- Membangun image Docker terbaru (`fikri-portfolio-app`). +- Menjalankan migrasi database Prisma secara otomatis. +- Melakukan pembersihan image lama untuk menghemat ruang disk. -## Architecture Philosophy +Aplikasi akan berjalan di port **4000**. -This portfolio demonstrates the same architectural principles I apply in all projects: -- **Clean folder structure** — scalable and maintainable -- **Server Components** — data fetching at the server level with graceful fallbacks -- **Type safety** — full TypeScript throughout -- **Separation of concerns** — UI components separate from data fetching -- **Graceful degradation** — fallback data when DB is not connected +## Architecture & Philosophy -## Deployment +Portfolio ini merefleksikan pengalaman saya sebagai **Fullstack Developer selama 3+ tahun** dalam membangun aplikasi web yang skalabel dan siap produksi: +- **Scalable Architecture**: Pemisahan yang jelas antara UI, logika bisnis, dan akses data. +- **Modern Next.js Features**: Memanfaatkan Server Components untuk performa maksimal dan SEO. +- **Type Safety**: Penggunaan TypeScript di seluruh bagian aplikasi (End-to-end). +- **Automated Deployment**: Konfigurasi Docker yang dioptimalkan untuk performa dan kemudahan maintenance. +- **Clean Code**: Fokus pada keterbacaan kode (*readability*) dan kemudahan pemeliharaan (*maintainability*). -Deploy to Vercel (recommended): +## Contact -1. Push to GitHub -2. Import to Vercel -3. Add `DATABASE_URL` environment variable -4. Deploy - -For PostgreSQL, use [Supabase](https://supabase.com) or [Neon](https://neon.tech) for free hosted PostgreSQL. +Update profile Anda di: +- `src/app/layout.tsx` — SEO metadata (name, description) +- `src/components/sections/...` — Konten section masing-masing diff --git a/deploy.sh b/deploy.sh index 6c9fd3f..97f52ed 100755 --- a/deploy.sh +++ b/deploy.sh @@ -1,51 +1,30 @@ #!/bin/bash -# Exit immediately if a command exits with a non-zero status +# Berhenti jika ada error set -e -echo "Starting Production Deployment..." +echo "🚀 Memulai Deployment Aplikasi (TANPA DATABASE)..." -# Load environment variables from .env if it exists +# Load environment variabel if [ -f .env ]; then - echo "Loading environment variables from .env..." + echo "📄 Loading .env..." export $(grep -v '^#' .env | xargs) fi -# Check if 'docker compose' (v2) or 'docker-compose' (v1) is available +# Deteksi perintah docker compose if docker compose version >/dev/null 2>&1; then COMPOSE_CMD="docker compose" -elif docker-compose version >/dev/null 2>&1; then +else COMPOSE_CMD="docker-compose" -else - echo "Error: Docker Compose not found. Please install Docker Compose first." - exit 1 fi -# Configuration -DB_CONTAINER_NAME="fikri-portfolio-db" -DB_PORT="5429" +echo "📦 Membangun dan menjalankan container aplikasi..." +# Langsung jalankan perintah docker-compose up +# --build memastikan image terbaru selalu dibuat +$COMPOSE_CMD up -d --build 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 not found on port ${DB_PORT}." - echo "Full deployment (Database + App)..." - $COMPOSE_CMD up -d --build -fi - -# 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..." +# Membersihkan image sampah +echo "🧹 Membersihkan image lama..." docker image prune -f -echo "Deployment script finished successfully!" +echo "✨ Deployment Selesai! Aplikasi berjalan di port 4000." diff --git a/docker-compose.yml b/docker-compose.yml index 8306160..19f6546 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,22 +1,4 @@ services: - db: - image: postgres:15-alpine - container_name: fikri-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: . @@ -27,7 +9,8 @@ services: ports: - "4000:4000" environment: - DATABASE_URL: postgresql://${POSTGRES_USER:-fikri}:${POSTGRES_PASSWORD:-fikri}@host.docker.internal:5429/${POSTGRES_DB:-fullstack-portfolio}?schema=public + # Aplikasi akan langsung menggunakan DATABASE_URL dari file .env (pointing ke DB existing Anda) + DATABASE_URL: ${DATABASE_URL} JWT_SECRET: ${JWT_SECRET} R2_TOKEN: ${R2_TOKEN} R2_ACCESS_KEY: ${R2_ACCESS_KEY} @@ -35,13 +18,7 @@ services: R2_ENDPOINT: ${R2_ENDPOINT} R2_BUCKET_NAME: ${R2_BUCKET_NAME} IMAGE_URL: ${IMAGE_URL} - depends_on: - db: - condition: service_healthy extra_hosts: - "host.docker.internal:host-gateway" command: > sh -c "npx prisma migrate deploy && node server.js" - -volumes: - postgres_data: