A personal website that integrates an AI intelligent chat assistant, a backend management system, internationalization support, and a dark mode.
| Category | Technology |
|---|---|
| Framework | Next.js 16 (App Router, Webpack) |
| Language | TypeScript 5 |
| Frontend | React 19 |
| Styling | Tailwind CSS 4 |
| Animations | Framer Motion 12 |
| Internationalization | next-intl 4 |
| Data Storage | Upstash Redis |
| File Storage | Vercel Blob |
| AI Integration | Vercel AI SDK 6 (Zhipu/OpenAI/Anthropic) |
| Authentication | JWT (jose) + bcryptjs |
| Nodemailer 8 | |
| Forms | React Hook Form 7 + Zod 4 |
| Rich Text | Tiptap 3 (About page) |
| Markdown | react-markdown + rehype-highlight + remark-gfm (Blog/Projects) |
| Image Processing | Sharp |
| PDF Parsing | unpdf (Resume text extraction) |
| PWA | Serwist 9 (Service Worker) |
| Deployment | Vercel |
personal-portfolio/
├── app/ # Next.js App Router
│ ├── [locale]/ # Internationalized routes
│ │ ├── page.tsx # Homepage (sections combined)
│ │ ├── blog/ # Blog list & detail (with loading skeletons, on-demand JetBrains Mono font)
│ │ ├── chat/ # Full-screen AI chat page
│ │ ├── projects/ # Project list & detail (with loading skeletons)
│ │ ├── layout.tsx # Internationalized layout
│ │ ├── error.tsx # Error page
│ │ ├── loading.tsx # Loading state
│ │ └── not-found.tsx # 404 page
│ ├── admin/ # Admin panel
│ │ ├── layout.tsx # Admin layout
│ │ ├── about/ # About management
│ │ ├── blog/ # Blog management
│ │ ├── contact/ # Contact management
│ │ ├── experience/ # Experience management
│ │ ├── hero/ # Hero management
│ │ ├── messages/ # Messages management
│ │ ├── projects/ # Projects management
│ │ ├── sensitive-words/ # Sensitive words management
│ │ ├── skills/ # Skills management
│ │ ├── stats/ # Stats management
│ │ └── change-password/ # Password change
│ ├── api/ # API routes
│ │ ├── admin/ # Admin API
│ │ ├── auth/ # Auth API (login/logout/verify/change password)
│ │ ├── chat/ # AI chat API
│ │ ├── contact/ # Contact form API
│ │ └── warmup/ # Warmup API (Vercel Cron scheduled calls)
│ ├── sw.ts # Serwist Service Worker entry
│ ├── layout.tsx # Root layout
│ ├── page.tsx # Root page (redirect to default language)
│ ├── globals.css # Global styles entry
│ ├── manifest.ts # PWA Manifest (shortcuts, adaptive icons)
│ ├── robots.ts # Robots.txt generation
│ ├── sitemap.ts # Sitemap generation
│ ├── error.tsx # Global error page
│ └── global-error.tsx # Global uncaught error page
├── components/
│ ├── admin/ # Admin components
│ │ ├── AdminErrorBoundary.tsx # Admin error boundary
│ │ ├── AdminHeader.tsx # Admin header
│ │ ├── AdminSidebar.tsx # Sidebar navigation
│ │ ├── ConfirmDialog.tsx # Confirm dialog
│ │ ├── DataTable.tsx # Data table
│ │ ├── ImageUpload.tsx # File upload (supports images and PDF)
│ │ ├── MarkdownEditor.tsx # Markdown editor
│ │ ├── MessageToast.tsx # Message toast
│ │ ├── RichTextEditor.tsx # Tiptap rich text editor (About page)
│ │ ├── SensitiveWordsEditor.tsx # Sensitive words editor
│ │ ├── SaveStatus.tsx # Save status indicator
│ │ └── SortableList.tsx # Sortable list
│ ├── ai/ # AI chat components
│ │ ├── ChatInput.tsx # Chat input (with stop generation support)
│ │ ├── ChatMessage.tsx # Message bubble
│ │ ├── ChatWidget.tsx # Floating chat window (mobile drag-to-close)
│ │ ├── ClientChatWidget.tsx # Client chat entry (idle-time lazy loading)
│ │ ├── LoginModal.tsx # Login modal
│ │ ├── MarkdownRenderer.tsx # Markdown renderer
│ │ ├── ModeToggle.tsx # Mode toggle
│ │ ├── QuickPrompts.tsx # Quick prompts
│ │ ├── TypingIndicator.tsx # Typing indicator
│ │ └── VirtualMessageList.tsx # Virtual scrolling message list
│ ├── blog/ # Blog components
│ │ ├── BlogDetailClient.tsx # Blog detail client
│ │ ├── BlogListClient.tsx # Blog list client
│ │ ├── MDXComponents.tsx # Markdown render & table of contents
│ │ └── PostCard.tsx # Post card
│ ├── layout/ # Layout components
│ │ ├── ClientPortfolioWrapper.tsx # Client page wrapper (with jelly scroll)
│ │ ├── ClientProviders.tsx # Client providers combined
│ │ ├── DynamicFavicon.tsx # Dynamic favicon
│ │ ├── Footer.tsx # Footer
│ │ ├── Header.tsx # Navigation header
│ │ ├── LanguageSwitcher.tsx # Language switcher
│ │ ├── Navigation.tsx # Navigation menu
│ │ ├── PWAInstallPrompt.tsx # PWA install prompt component
│ │ ├── PortfolioPageWrapper.tsx # Page wrapper
│ │ ├── ThemeProvider.tsx # Theme provider
│ │ └── ThemeToggle.tsx # Theme toggle
│ ├── sections/ # Homepage section components
│ │ ├── HeroSection.tsx # Hero section
│ │ ├── HeroSectionWithData.tsx # Hero section (server component, supports Suspense)
│ │ ├── AboutSection.tsx # About section
│ │ ├── AboutSectionWithData.tsx # About section (server component, supports Suspense)
│ │ ├── SkillsSection.tsx # Skills section
│ │ ├── SkillsSectionWithData.tsx # Skills section (server component, supports Suspense)
│ │ ├── StatsSection.tsx # Stats section
│ │ ├── StatsSectionWithData.tsx # Stats section (server component, supports Suspense)
│ │ ├── ProjectsSection.tsx # Projects section
│ │ ├── ProjectsSectionWithData.tsx # Projects section (server component, supports Suspense)
│ │ ├── ExperienceSection.tsx # Experience section
│ │ ├── ExperienceSectionWithData.tsx # Experience section (server component, supports Suspense)
│ │ ├── BlogSection.tsx # Blog section
│ │ ├── BlogSectionWithData.tsx # Blog section (server component, supports Suspense)
│ │ ├── ContactSection.tsx # Contact section
│ │ ├── ContactSectionWithData.tsx # Contact section (server component, supports Suspense)
│ │ ├── ProjectCard.tsx # Project card
│ │ ├── ProjectDetailContent.tsx # Project detail content
│ │ ├── SectionSkeleton.tsx # Skeleton loader (Hero, About, Skills, Stats, Projects, Experience, Blog, Contact)
│ │ └── TimelineItem.tsx # Timeline item
│ ├── seo/ # SEO components
│ │ └── JsonLd.tsx # JSON-LD structured data (Person, WebSite, Article, Breadcrumb)
│ └── ui/ # Common UI components
│ ├── AnimatedCounter.tsx # Animated counter
│ ├── AnimatedLine.tsx # Animated line
│ ├── Badge.tsx # Badge
│ ├── Button.tsx # Button
│ ├── ContactForm.tsx # Contact form
│ ├── DatePicker.tsx # Date picker
│ ├── Dialog.tsx # Dialog
│ ├── FloatingParticles.tsx # Floating particles
│ ├── Input.tsx # Input
│ ├── MagneticButton.tsx # Magnetic button
│ ├── MarkdownRenderer.tsx # Markdown renderer
│ ├── MouseGlow.tsx # Mouse glow
│ ├── OptimizedJellyScroll.tsx # Jelly scroll component
│ ├── ScrollToTopButton.tsx # Back to top
│ ├── Select.tsx # Select
│ ├── Skeleton.tsx # Skeleton loader
│ ├── Textarea.tsx # Textarea
│ └── TiltCard.tsx # 3D tilt card
├── hooks/ # Custom hooks
│ ├── useChat.ts # AI chat logic
│ └── usePWAInstall.ts # PWA install hook
├── i18n/ # Internationalization
│ ├── zh-CN.json # Chinese translations
│ ├── en-US.json # English translations
│ ├── routing.ts # Routing config
│ ├── navigation.ts # Navigation utilities
│ ├── request.ts # Request config (with messages caching)
│ └── server.ts # Server-side i18n utilities
├── lib/ # Utility library
│ ├── ai/ # AI related
│ │ ├── providers/ # AI provider implementations
│ │ │ ├── zhipu.ts # Zhipu AI (SSE stream parsing, timeout protection, buffer overflow handling)
│ │ │ ├── openai.ts # OpenAI
│ │ │ ├── anthropic.ts # Anthropic
│ │ │ ├── types.ts # Type definitions
│ │ │ └── index.ts # Provider registry
│ │ ├── context.ts # Context building (with resume content integration)
│ │ ├── context-compressor.ts # Context compression (compact format, ~60% token reduction)
│ │ ├── redact.ts # Resume content redaction (user-configured sensitive word mapping replacement)
│ │ ├── extractor.ts # User preference/need extraction
│ │ ├── summarizer.ts # Conversation summarization (auto-compress long conversations)
│ │ ├── tokenizer.ts # Token estimation and context window management
│ │ ├── utils.ts # AI utility functions (stream collection, etc.)
│ │ ├── rate-limit.ts # Rate limiting
│ │ ├── resume-parser.ts # Resume text retrieval
│ │ └── system-prompts.ts # System prompts (with 30-second cache, admin panel security warnings)
│ ├── auth/ # Authentication
│ │ ├── index.ts # Auth utilities (with withAuth wrapper)
│ │ ├── jwt.ts # JWT utilities (with token caching, LRU eviction strategy)
│ │ └── middleware.ts # Auth middleware
│ ├── i18n/ # i18n utilities
│ │ └── server.ts # Server-side translation and language parsing
│ ├── api.ts # Server-side data fetching layer
│ ├── api-client.ts # Client-side API request utilities
│ ├── constants.ts # Shared constants (default skill categories, etc.)
│ ├── data.ts # Upstash Redis data read/write (LRU cache, atomic append, development mode performance logging)
│ ├── framer-optimizations.tsx # Framer Motion performance optimizations (LazyMotion, OptimizedMotionDiv)
│ ├── notification.ts # Notification push
│ ├── rate-limit.ts # General rate limiting (Redis Lua atomic scripts)
│ ├── redis.ts # Redis singleton (unified connection management)
│ ├── revalidate.ts # Cache revalidation utilities
│ ├── scroll-session.ts # Scroll session restore (cross-page anchor navigation, TTL expiration)
│ └── utils.ts # Common utilities (getLocaleKey, getClientIp, errorResponse, formatDateTime)
├── types/ # TypeScript types
│ └── index.ts # Global type definitions
├── styles/ # Styles
│ └── globals.css # Global styles (CSS variables, animations, scrollbar, etc.)
├── public/ # Static assets
│ ├── icons/ # Icons directory (custom icons like UniApp)
│ └── images/ # Images directory
├── scripts/ # Scripts
│ └── generate-password-hash.js # Password hash generator
├── next.config.ts # Next.js config
├── proxy.ts # Next.js middleware (i18n routing + admin auth protection)
├── vercel.json # Vercel deployment config
├── .env.example # Environment variables template
└── package.json # Project dependencies
# Clone the project
git clone https://github.com/Adou377/personal-portfolio.git
cd personal-portfolio
# Install dependencies
pnpm install
# Copy environment variables
cp .env.example .env.local
# Edit .env.local and fill in actual configuration
Copy .env.example to .env.local, then fill in according to the following instructions:
| Variable | Description | Example |
|---|---|---|
NEXT_PUBLIC_SITE_URL | Site domain | https://trend-flow.top |
NEXT_PUBLIC_DEFAULT_LOCALE | Default language | zh-CN |
| Variable | Description | How to Get |
|---|---|---|
UPSTASH_REDIS_REST_URL | Upstash Redis REST URL | Upstash Console |
UPSTASH_REDIS_REST_TOKEN | Upstash Redis Token | Upstash Console |
BLOB_READ_WRITE_TOKEN | Vercel Blob read/write token | Vercel Storage |
| Variable | Description |
|---|---|
AI_PROVIDER | AI provider: zhipu / openai / anthropic |
ZHIPU_API_KEY | Zhipu AI API Key |
ZHIPU_MODEL | Zhipu model name (e.g., glm-4.7-flash) |
ZHIPU_BASE_URL | Zhipu API URL |
OPENAI_API_KEY | OpenAI API Key |
OPENAI_MODEL | OpenAI model name |
ANTHROPIC_API_KEY | Anthropic API Key |
ANTHROPIC_MODEL | Anthropic model name |
| Variable | Description | Default |
|---|---|---|
AI_RATE_LIMIT_HOURLY | Hourly request limit | 20 |
AI_RATE_LIMIT_DAILY | Daily request limit | 100 |
AI_DAILY_BUDGET_TOKENS | Daily token budget | 100000 |
| Variable | Description |
|---|---|
SMTP_HOST | SMTP server address |
SMTP_PORT | SMTP port |
SMTP_USER | SMTP username |
SMTP_PASS | SMTP authorization code |
CONTACT_EMAIL | Email to receive notifications |
BARK_SERVER_URL | Bark push URL |
BARK_ICON_URL | Bark icon URL |
BARK_SOUND | Bark sound |
SERVERCHAN_SENDKEY | ServerChan SendKey |
SERVERCHAN_CHANNEL | ServerChan push channel |
| Variable | Description |
|---|---|
ADMIN_PASSWORD_HASH | bcrypt hash of admin password |
JWT_SECRET | JWT signing secret (at least 32 characters) |
JWT_EXPIRES_IN | Token validity (e.g., 24h, 7d) |
ADMIN_LOGIN_MAX_ATTEMPTS | Max login attempts |
ADMIN_LOGIN_LOCKOUT_MINUTES | Login lockout duration (minutes) |
| Variable | Description | Default |
|---|---|---|
UPLOAD_MAX_SIZE | Max upload file size (MB) | 5 |
node scripts/generate-password-hash.js
This will generate a password hash and JWT secret, fill them into .env.local.
pnpm dev
Visit http://localhost:3000 to view the website.
pnpm build
pnpm lint
UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKENBLOB_READ_WRITE_TOKENProject already includes vercel.json configuration with security headers (including CSP), Japan region deployment, and Cron scheduled tasks (server warmup every 5 minutes).
Project configures the following security headers via vercel.json:
Cache-Control: public, max-age=0, must-revalidate - HTML pages no-cacheX-Content-Type-Options: nosniffX-Frame-Options: DENYX-XSS-Protection: 1; mode=blockReferrer-Policy: strict-origin-when-cross-originContent-Security-Policy - Restricts script, style, image, and connection sources (supports Vercel Blob, SimpleIcons CDN)Cache-Control: no-storeCache-Control: public, max-age=31536000, immutableVisit /admin to access the admin panel, log in with the configured password.
Project uses Upstash Redis for structured data (JSON), and Vercel Blob for uploaded files (images, PDFs, etc.). This architecture is suitable for Serverless deployment environments, with persistent and scalable data storage.
When uploading a PDF resume, the system automatically extracts text content and stores it in contact.json. The AI chat resume mode prioritizes the extracted resume text as context.
Edit CSS variables in styles/globals.css:
:root {
--primary: #8FB2C9;
--accent: #E94560;
--ai-glow: #7C3AED;
/* ... */
}
.dark {
--primary: #8FB2C9;
--accent: #E94560;
--ai-glow: #A78BFA;
/* ... */
}
Modify all personal data through the admin panel.
locales in i18n/routing.tsi18n/ja-JP.json)Locale type in types/index.ts