Skip to content

ddrayko/portfolio

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

353 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Drayko Logo

Drayko v6 Portfolio

High-performance portfolio platform built with Next.js 16, Drizzle ORM, and PostgreSQL. Features a dynamic project showcase, interactive timeline, admin dashboard, versioning system, and real-time maintenance controls.


Tech Stack

Category Technology
Framework Next.js 16.2 (App Router, Turbopack)
Language TypeScript 5 (strict mode)
Styling Tailwind CSS v4, tw-animate-css, OKLCH color space
Database PostgreSQL (via Neon serverless)
ORM Drizzle ORM 0.45
Auth Custom cookie-based (bcryptjs, httpOnly sessions, 24h expiry)
UI Primitives Radix UI (20+ components), shadcn/ui style
Icons Lucide React
Fonts Inter (sans), Outfit (display) via next/font/google
Notifications Sonner
Deployment Vercel (production), Codeberg (source)
Infrastructure OVHcloud VPS, Nginx, PM2, Docker

Architecture

flowchart LR
    subgraph Client["Client Layer"]
        Browser["Browser"]
        React["React 19 + RSC"]
    end

    subgraph NextJS["Next.js 16 App Router"]
        Layout["layout.tsx β€” fonts, footer, popups"]
        Pages["13 routes: / /about /contact /journey /update /privacy /terms /copyright /tags-info /maintenance /[slug]/update /admin + dashboard"]
        API["Server actions β€” CRUD projects, admins, moments, versions, settings, site updates"]
    end

    subgraph Components["Components"]
        Public["Public β€” ProjectCard, TechStack, PortfolioContent, MomentTimeline, TagFilter, VersionSelector"]
        Admin["Admin β€” DashboardClient, Forms, Dialogs, Toggles"]
        UI["UI primitives β€” 20 Radix + shadcn/ui components"]
    end

    subgraph Data["Data Layer"]
        Drizzle["Drizzle ORM"]
        DB[("PostgreSQL β€” 6 tables")]
        Schema["projects Β· admins Β· settings Β· site_updates Β· moments Β· versions"]
    end

    Client --> NextJS
    NextJS --> Components
    NextJS --> API
    API --> Drizzle
    Drizzle --> DB
    Components --> UI
Loading

Routes

Route Type Description
/ Dynamic Homepage β€” hero (badge links to /update or custom URL), project grid, tech stack marquee, CTA
/about Dynamic Developer story, philosophy cards, personal note
/contact Dynamic Contact info, availability status, email copy
/journey Dynamic Animated timeline of education, work, and milestones
/update Dynamic System roadmap with countdown + changelog history
/[slug]/update Dynamic Per-project changelog (auto: /update for drayko.xyz)
/admin Dynamic Admin login form (cookie-based session)
/admin/dashboard Dynamic Full admin panel β€” CRUD for all entities
/maintenance Dynamic Maintenance mode display (server-redirected)
/privacy Static Privacy policy
/terms Static Terms of service
/copyright Static Copyright & license info
/tags-info Dynamic Tag/status legend for project badges
/sitemap.xml Generated Dynamic sitemap (8 routes, weekly/monthly)
/robots.txt Generated Disallows /admin, /maintenance, /update

Database Schema

erDiagram
    projects {
        serial id PK
        text title
        text slug UK
        text description
        text image_url
        text[] tags
        text project_url
        text github_url
        boolean in_development
        text development_status
        boolean is_completed
        boolean is_archived
        integer development_progress
        jsonb changelog
        timestamp created_at
    }

    admins {
        serial id PK
        text email UK
        text password
        timestamp created_at
    }

    settings {
        text key PK
        jsonb value
        timestamp updated_at
    }

    site_updates {
        serial id PK
        timestamp next_update_date
        boolean no_update_planned
        jsonb planned_features
        jsonb changelog
        text latest_update_text
        boolean show_last_update_prefix
        text hero_link_type
        text hero_custom_url
        timestamp updated_at
    }

    moments {
        serial id PK
        text title
        text description
        text date
        text type
        text icon
        timestamp created_at
    }

    versions {
        serial id PK
        text name
        text description
        text link
        boolean is_current
        timestamp created_at
    }
Loading

Component Tree

flowchart LR
    subgraph Public["Public Pages"]
        Home["Home Page
              page.tsx"]
        About["About Page
              about/page.tsx"]
        Contact["Contact Page
                contact/page.tsx"]
        Journey["Journey Page
                journey/page.tsx"]
        Update["Update Page
               update/page.tsx"]
    end

    Home --> Portfolio["PortfolioContent
                       client component"]
    Home --> Tech["TechStack
                  client component"]
    Home --> Version["VersionSelector"]
    Home --> PF["ProjectCard
                (per project)"]

    Portfolio --> Tag["TagFilter"]
    Portfolio --> Search["Search bar"]

    Tech --> TechCard["TechCard
                       (47 technologies, 3 marquee rows)"]

    Journey --> Timeline["MomentTimeline
                         client component"]
    Timeline --> MomentCard["Moment Card
                             (education/work/milestone)"]

    Update --> Changelog["ChangelogList"]
    Update --> CountdownComponent["Countdown
                                   (Odometer.js CDN)"]

    Contact --> CopyBtn["CopyEmail
                         clipboard button"]
Loading
flowchart LR
    subgraph Admin["Admin /dashboard"]
        Dashboard["AdminDashboardClient
                  531-line client component"]
    end

    Dashboard --> Sections["Accordion Sections"]
    
    Sections --> Section1["Projects
                           CRUD + cards"]
    Sections --> Section2["Moments
                           CRUD + cards"]
    Sections --> Section3["Versions
                           CRUD + cards"]
    Sections --> Section4["Admins
                           CRUD + cards"]
    Sections --> Section5["Site Settings
                           UpdateDialog
                           BadgeDialog (headline + prefix + link destination)
                           MaintenanceToggle
                           AvailabilityToggle"]

    Section1 --> ProjDialog["ProjectDialog"]
    Section1 --> ProjForm["ProjectForm
                           (2 tabs: details + changelog)"]
    Section1 --> ProjCard["AdminProjectCard"]
    Section1 --> DeleteProj["DeleteProjectDialog"]

    Section2 --> MomDialog["MomentDialog"]
    Section2 --> MomForm["MomentForm"]
    Section2 --> MomCard["AdminMomentCard"]

    Section3 --> VerDialog["VersionDialog"]
    Section3 --> VerForm["VersionForm"]
    Section3 --> VerCard["AdminVersionCard"]

    Section4 --> AdmDialog["AdminDialog"]
    Section4 --> AdmForm["AdminForm"]
    Section4 --> AdmCard["AdminCard"]
    Section4 --> DeleteAdm["DeleteAdminDialog"]
Loading

Data Flow

sequenceDiagram
    participant B as Browser
    participant S as Next.js Server
    participant D as PostgreSQL

    Note over B,S: Public page request
    B->>S: GET / (any page)
    S->>S: checkMaintenance() + isLocalRequest()
    alt Maintenance ON & not local
        S->>B: 302 Redirect /maintenance
    else Normal
        S->>D: SELECT projects, versions, site_updates
        D-->>S: data rows
        S->>S: Serialize dates (toISOString)
        S-->>B: Rendered HTML + RSC payload
    end

    Note over B,S: Admin login
    B->>S: POST login (email, password)
    S->>D: SELECT admin WHERE email = ?
    S->>S: bcrypt.compare(password, hash)
    alt Valid
        S->>S: Set httpOnly cookie (24h)
        S->>B: 302 Redirect /admin/dashboard
    else Invalid
        S-->>B: { success: false, error }
    end

    Note over B,S: Admin CRUD
    B->>S: Server Action (create/update/delete)
    S->>S: checkAdminSession()
    S->>D: INSERT / UPDATE / DELETE
    D-->>S: result
    S->>S: revalidatePath("/")
    S-->>B: { success: true, data }
Loading

Features

Public

  • Dynamic Project Grid β€” Searchable, filterable by tags, responsive 3-column grid
  • Project Cards β€” Rich status indicators (WIP, paused, finished, archived) with progress bars, live demo/code links, and update history
  • Tech Stack Marquee β€” 47 technology icons in 3 auto-scrolling rows with hover pause
  • Journey Timeline β€” Alternating left/right cards with Lucide icons, animated on viewport
  • System Roadmap β€” Next-update countdown (Odometer.js) + versioned changelog
  • Hero Badge β€” Dynamic badge with configurable link destination (update page or custom URL)
  • Version Selector β€” Dropdown to switch between portfolio versions with old-version detection popup
  • Availability Badge β€” Real-time "Open for projects" indicator on contact page
  • Maintenance Mode β€” Server-side redirect with bypass for local requests

Admin

  • Full CRUD β€” Projects, Moments, Versions, Admins
  • Project Editor β€” Two-tab form (details + reorderable changelog)
  • Site Updates β€” Edit roadmap, changelog, planned features, badge text, and badge link destination (update page / custom URL)
  • Maintenance Toggle β€” Enable/disable site-wide with custom message and ETA
  • Availability Toggle β€” "Open for projects" switch
  • Version Management β€” Create named versions with links (e.g., v5, v6-beta)

Technical

  • Cookie-based Auth β€” httpOnly, secure (production), sameSite=lax, 24h expiry
  • Lazy DB Proxy β€” Postgres connection deferred until first query; mock fallback in dev without DATABASE_URL
  • TypeScript Strict β€” Full strict mode with build-time type checking disabled
  • Force-dynamic β€” All pages server-rendered on each request (no stale data)
  • Dynamic Sitemap β€” Auto-generated sitemap.xml + robots.txt
  • Console Easter Egg β€” Hidden message in browser devtools

Performance

Build Time (Vercel)

Before:          22–30s
After cleanup:   ~8–12s (estimated on Vercel)
Local build:     ~3s (compilation: 2.7s)

Optimizations applied:

  • Removed 28 unused dependencies (91 packages removed from node_modules)
  • Moved type packages to devDependencies
  • typescript.ignoreBuildErrors: true
  • npm ci --prefer-offline on Vercel installs
  • Turbopack (default in Next.js 16)

Getting Started

Prerequisites

  • Node.js 20+
  • PostgreSQL database (or Neon serverless)
  • npm

Environment Variables

# Required
DATABASE_URL=postgresql://user:password@host:5432/db
# or
POSTGRES_URL=postgresql://user:password@host:5432/db

Install & Run

git clone https://codeberg.org/ddrayko/v6-portfolio.git
cd v6-portfolio
npm install
npm run dev        # http://localhost:3000

Build

npm run build
npm start          # production server on :3000

Database

npm run db:push       # Push schema to database
npm run db:generate   # Generate migration files
npm run db:studio     # Open Drizzle Studio GUI

Seed scripts are in scripts/:

  • 001_create_projects_table.sql to 005_update_admin_password.sql
  • hash-password.js β€” generate bcrypt hashes for admin passwords

Project Structure

v6-portfolio/
β”œβ”€β”€ app/                  # Next.js App Router pages
β”‚   β”œβ”€β”€ layout.tsx        # Root layout (fonts, footer, popups)
β”‚   β”œβ”€β”€ page.tsx          # Homepage
β”‚   β”œβ”€β”€ about/
β”‚   β”œβ”€β”€ contact/
β”‚   β”œβ”€β”€ journey/
β”‚   β”œβ”€β”€ update/
β”‚   β”œβ”€β”€ admin/
β”‚   β”‚   β”œβ”€β”€ page.tsx      # Login
β”‚   β”‚   └── dashboard/    # Admin panel
β”‚   β”œβ”€β”€ [slug]/update/    # Per-project changes
β”‚   └── sitemap.ts        # Dynamic sitemap
β”œβ”€β”€ components/
β”‚   β”œβ”€β”€ ui/               # 20 shadcn/ui primitives
β”‚   β”œβ”€β”€ project-card.tsx
β”‚   β”œβ”€β”€ tech-stack.tsx
β”‚   β”œβ”€β”€ portfolio-content.tsx
β”‚   β”œβ”€β”€ moment-timeline.tsx
β”‚   └── ... (33 custom components)
β”œβ”€β”€ db/
β”‚   β”œβ”€β”€ schema.ts         # 6 Drizzle tables
β”‚   └── index.ts          # Lazy DB client
β”œβ”€β”€ lib/
β”‚   β”œβ”€β”€ actions.ts        # 16 server actions
β”‚   β”œβ”€β”€ admin-auth.ts     # Cookie auth system
β”‚   β”œβ”€β”€ server-utils.ts   # Local request detection
β”‚   β”œβ”€β”€ types.ts          # TypeScript interfaces
β”‚   └── utils.ts          # cn() helper
β”œβ”€β”€ hooks/
β”‚   β”œβ”€β”€ use-mobile.ts
β”‚   └── use-toast.ts
β”œβ”€β”€ public/
β”‚   β”œβ”€β”€ assets/tech/      # 47 technology icons
β”‚   └── ...               # Favicon, logos
β”œβ”€β”€ scripts/              # SQL migrations + utilities
β”œβ”€β”€ next.config.mjs
β”œβ”€β”€ vercel.json
└── package.json

License

This project is free software β€” see COPYRIGHT.md for details.

About

πŸ€“β€‹ My portfolio

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Contributors