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.
| 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 |
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
| 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 |
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
}
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"]
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"]
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 }
- 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
- 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)
- 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
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: truenpm ci --prefer-offlineon Vercel installs- Turbopack (default in Next.js 16)
- Node.js 20+
- PostgreSQL database (or Neon serverless)
- npm
# Required
DATABASE_URL=postgresql://user:password@host:5432/db
# or
POSTGRES_URL=postgresql://user:password@host:5432/dbgit clone https://codeberg.org/ddrayko/v6-portfolio.git
cd v6-portfolio
npm install
npm run dev # http://localhost:3000npm run build
npm start # production server on :3000npm run db:push # Push schema to database
npm run db:generate # Generate migration files
npm run db:studio # Open Drizzle Studio GUISeed scripts are in scripts/:
001_create_projects_table.sqlto005_update_admin_password.sqlhash-password.jsβ generate bcrypt hashes for admin passwords
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
This project is free software β see COPYRIGHT.md for details.
