Some checks failed
Dev Deployment (Zero Downtime) / deploy-dev (push) Failing after 9m26s
8.0 KiB
8.0 KiB
Portfolio Project Instructions
This is Dennis Konkol's personal portfolio (dk0.dev) - a Next.js 15 portfolio with Directus CMS integration, n8n automation, and a "liquid" design system.
Build, Test, and Lint
Development
npm run dev # Full dev environment (Docker + Next.js)
npm run dev:simple # Next.js only (no Docker dependencies)
npm run dev:next # Plain Next.js dev server
Build & Deploy
npm run build # Production build (standalone mode)
npm run start # Start production server
Testing
# Unit tests (Jest)
npm run test # Run all unit tests
npm run test:watch # Watch mode
npm run test:coverage # With coverage report
# E2E tests (Playwright)
npm run test:e2e # Run all E2E tests
npm run test:e2e:ui # Interactive UI mode
npm run test:critical # Critical paths only
npm run test:hydration # Hydration tests only
Linting
npm run lint # Run ESLint
npm run lint:fix # Auto-fix issues
Database (Prisma)
npm run db:generate # Generate Prisma client
npm run db:push # Push schema to database
npm run db:studio # Open Prisma Studio
npm run db:seed # Seed database
Architecture Overview
Tech Stack
- Framework: Next.js 15 (App Router), TypeScript 5.9
- Styling: Tailwind CSS 3.4 with custom
liquid-*color tokens - Theming: next-themes for dark mode (system/light/dark)
- Animations: Framer Motion 12
- 3D: Three.js + React Three Fiber (shader gradient background)
- Database: PostgreSQL via Prisma ORM
- Cache: Redis (optional)
- CMS: Directus (self-hosted, GraphQL, optional)
- Automation: n8n webhooks (status, chat, hardcover, image generation)
- i18n: next-intl (EN + DE)
- Monitoring: Sentry
- Deployment: Docker (standalone mode) + Nginx
Key Directories
app/
[locale]/ # i18n routes (en, de)
page.tsx # Homepage sections
projects/ # Project listing + detail pages
api/ # API routes
book-reviews/ # Book reviews from Directus
hobbies/ # Hobbies from Directus
n8n/ # n8n webhook proxies
projects/ # Projects (PostgreSQL + Directus)
tech-stack/ # Tech stack from Directus
components/ # React components
lib/
directus.ts # Directus GraphQL client (no SDK)
auth.ts # Auth + rate limiting
translations-loader.ts # i18n loaders for server components
prisma/
schema.prisma # Database schema
messages/
en.json # English translations
de.json # German translations
Data Source Fallback Chain
The architecture prioritizes resilience with this fallback hierarchy:
- Directus CMS (if
DIRECTUS_STATIC_TOKENconfigured) - PostgreSQL (for projects, analytics)
- JSON files (
messages/*.json) - Hardcoded defaults
- Display key itself (last resort)
Critical: The site never crashes if external services (Directus, PostgreSQL, n8n, Redis) are unavailable. All API routes return graceful fallbacks.
CMS Integration (Directus)
- GraphQL calls via
lib/directus.ts(no Directus SDK) - Collections:
tech_stack_categories,tech_stack_items,hobbies,content_pages,projects,book_reviews - Translations use Directus native system (M2O to
languages) - Locale mapping:
en→en-US,de→de-DE - API routes export
runtime='nodejs',dynamic='force-dynamic'and include asourcefield in JSON responses (directus|fallback|error)
n8n Integration
- Webhook base URL:
N8N_WEBHOOK_URLenv var - Auth via
N8N_SECRET_TOKENand/orN8N_API_KEYheaders - All endpoints have rate limiting and 10s timeout protection
- Hardcover reading data cached for 5 minutes
Key Conventions
i18n (Internationalization)
- Supported locales:
en(English),de(German) - Primary source: Static JSON files in
messages/en.jsonandmessages/de.json - Optional override: Directus CMS
messagescollection - Server components: Use
getHeroTranslations(),getNavTranslations(), etc. fromlib/translations-loader.ts - Client components: Use
useTranslations("key.path")from next-intl - Locale mapping: Middleware defines
["en", "de"]which must matchapp/[locale]/layout.tsx
Component Patterns
- Client components: Mark with
"use client"for interactive/data-fetching parts - Data loading: Use
useEffectfor client-side fetching on mount - Animations: Framer Motion
variantspattern withstaggerContainer+fadeInUp - Loading states: Every async component needs a matching Skeleton component
Design System ("Liquid Editorial Bento")
- Core palette: Cream (
#fdfcf8), Stone (#0c0a09), Emerald (#10b981) - Custom colors: Prefixed with
liquid-*(sky, mint, lavender, pink, rose, peach, coral, teal, lime) - Card style: Gradient backgrounds (
bg-gradient-to-br from-liquid-*/15 via-liquid-*/10 to-liquid-*/15) - Glassmorphism: Use
backdrop-blur-smwithborder-2androunded-xl - Typography: Headlines uppercase, tracking-tighter, with accent point at end
- Layout: Bento Grid for new features (no floating overlays)
File Naming
- Components: PascalCase in
app/components/(e.g.,About.tsx) - API routes: kebab-case directories in
app/api/(e.g.,book-reviews/) - Lib utilities: kebab-case in
lib/(e.g.,email-obfuscate.ts)
Code Style
- Language: Code in English, user-facing text via i18n
- TypeScript: No
anytypes - use interfaces fromlib/directus.tsorapp/_ui/ - Error handling: All API calls must catch errors with fallbacks
- Error logging: Only in development mode (
process.env.NODE_ENV === "development") - Commit messages: Conventional Commits (
feat:,fix:,chore:) - No emojis: Unless explicitly requested
Testing Notes
- Jest environment: JSDOM with mocks for
window.matchMediaandIntersectionObserver - Playwright: Uses plain Next.js dev server (no Docker) with
NODE_ENV=developmentto avoid Edge runtime issues - Transform: ESM modules (react-markdown, remark-*, etc.) are transformed via
transformIgnorePatterns - After UI changes: Run
npm run testto verify no regressions
Docker & Deployment
- Standalone mode:
next.config.tsusesoutput: "standalone"for optimized Docker builds - Branches:
dev→ staging,production→ live - CI/CD: Gitea Actions (
.gitea/workflows/) - Verify Docker builds: Always test Docker builds after changes to
next.config.tsor dependencies
Common Tasks
Adding a CMS-managed section
- Define GraphQL query + types in
lib/directus.ts - Create API route in
app/api/<name>/route.tswithruntime='nodejs'anddynamic='force-dynamic' - Create component in
app/components/<Name>.tsx - Add i18n keys to
messages/en.jsonandmessages/de.json - Integrate into parent component
Adding i18n strings
- Add keys to both
messages/en.jsonandmessages/de.json - Use
useTranslations("key.path")in client components - Use
getTranslations("key.path")in server components
Working with Directus
- All queries go through
directusRequest()inlib/directus.ts - Uses GraphQL endpoint (
/graphql) with 2s timeout - Returns
nullon failure (graceful degradation) - Translations filtered by
languages_code.codematching Directus locale
Environment Variables
Required for CMS
DIRECTUS_URL=https://cms.dk0.dev
DIRECTUS_STATIC_TOKEN=...
Required for n8n features
N8N_WEBHOOK_URL=https://n8n.dk0.dev
N8N_SECRET_TOKEN=...
N8N_API_KEY=...
Database & Cache
DATABASE_URL=postgresql://...
REDIS_URL=redis://...
Optional
SENTRY_DSN=...
NEXT_PUBLIC_BASE_URL=https://dk0.dev
Documentation References
- Operations guide:
docs/OPERATIONS.md - Locale system:
docs/LOCALE_SYSTEM.md - CMS guide:
docs/CMS_GUIDE.md - Testing & deployment:
docs/TESTING_AND_DEPLOYMENT.md