ViteReact作品集

fiqtor.com-create-your-own — Vite 作品集模板

作者 FIQTOR

基于 React 与 Vite 前端、Node.js 与 Express 后端的全栈个人作品集模板,仓库通过 Git 子模块组织前后端代码,提供 AI 聊天、联系表单、GitHub 与 WakaTime 统计、项目与证书展示等功能。

看看你的网站会是什么样

浏览真实页面截图,比较内容排版与手机效果。点击下方缩略图切换,放大查看整页细节。

1 个预览视图
作者提供的预览
作者提供的预览

来自项目 README 的原始预览。

目前有 1 张原始预览,可打开完整图片查看设计细节。

截图用于了解视觉与布局,交互效果请以实际项目为准。

采集于 2026-10-06查看来源

值得关注的模板亮点

从已有项目资料中,快速了解它的特点。

01

伞形仓库通过 Git 子模块组织前后端,frontend/ 与 backend/ 各自独立

02

AI 助手支持语音听写、文件附件、流式回复与复制/重新生成

03

联系表单通过 WhatsApp Business API 发送,失败时回退 SMTP 邮件

04

展示 GitHub 贡献图与 WakaTime 编码活动

05

包含项目、职业时间线、证书、技能、服务与多语言简历等板块

06

内容与品牌信息集中在配置文件和环境变量中,无需修改组件

模板详细介绍

这是一个用于搭建个人作品集网站的全栈模板。仓库本身是伞形(umbrella)仓库,不包含应用代码,而是通过 Git 子模块引用两个独立仓库:frontend/ 指向 React + Vite 前端,backend/ 指向 Node.js + Express API。前端负责页面展示与内容组织,后端提供 AI 聊天、联系表单、GitHub 与 WakaTime 统计等接口。

模板包含的页面与模块有:项目展示、职业时间线、证书、技能、服务、多语言简历,以及一个经典 linktree 风格的页面。AI 助手支持语音听写、文件附件、流式回复和复制/重新生成操作;联系表单通过 WhatsApp Business API 发送通知,失败时回退到 SMTP 邮件。GitHub 贡献图和 WakaTime 编码活动由后端接口获取。

内容定制方式:个人与品牌信息放在 frontend/src/config/Identity.ts 和少量 VITE_* 环境变量中;作品集内容(项目、证书、职业经历)放在 frontend/src/data/*.ts;页面 head 元数据在构建时由 src/config/Head.ts 生成。后端身份与社交信息在 backend/src/config/identity.js 中配置。

使用限制:仓库采用 MIT 许可,可免费使用、修改、商用和分发该版本代码,但需保留版权与许可声明。演示图片、字体、图标等第三方素材需单独核对许可。域名、托管、数据库、AI 服务等外部服务可能产生费用。VITE_ 前缀的环境变量会进入浏览器公开包,不能存放密钥。

ViteReact作品集

适合你的项目吗?

适用场景

  • 搭建个人作品集网站,集中展示项目、职业经历与证书
  • 在个人站点中集成 AI 助手,回答访客关于自己的问题
  • 通过 WhatsApp 或邮件接收访客的联系表单提交
  • 在个人主页展示 GitHub 贡献与 WakaTime 编码统计
  • 作为学习 React + Vite 与 Node.js + Express 全栈结构的参考项目

适合人群

  • 需要快速搭建个人作品集的前端或全栈开发者
  • 希望展示 GitHub 与 WakaTime 数据的开发者
  • 需要多语言简历与职业时间线展示的求职者
  • 希望以 MIT 许可自由修改和分发代码的用户

上手与定制指南

结合仓库当前版本的说明,按下面的资料检查内容、配置与运行要求。

  1. 1

    克隆时需使用 --recurse-submodules 或随后执行 git submodule update --init --recursive,否则 backend/ 与 frontend/ 目录为空

  2. 2

    需分别进入 backend 和 frontend 目录执行 npm install,并各自复制 .env.example 为 .env 填写配置

  3. 3

    后端需要 Node.js v20 或更高版本,并配置 Gemini API Key、SMTP 等环境变量

  4. 4

    VITE_ 前缀的环境变量会进入浏览器公开包,不能放置真实密钥

  5. 5

    演示图片、字体、图标等第三方素材需单独核对许可,外部服务可能产生费用

查看仓库使用说明

授权与来源资料

使用条件与限制

免费使用 · 保留许可声明

本次收录的是公开仓库中采用 MIT 许可的免费版本;免费范围为该版本代码,不代表外部服务或第三方素材免费。

使用时需要遵守

  • 允许免费使用、修改、商用和分发该版本代码。
  • 分发代码副本或实质性部分时,保留原作者版权声明和完整 MIT 许可声明。
  • 代码按现状提供;担保与责任限制以原许可证及适用法律为准。

额外费用与待核对事项

  • 演示图片、字体、图标及其他第三方素材需按各自许可核对,不能仅凭代码的 MIT 许可判断。
  • 域名、托管、数据库、支付或 AI 等外部服务可能另外收费。
  • 该仓库本身不含应用代码,仅通过 Git 子模块引用前后端两个独立仓库;使用时必须递归克隆子模块,否则 backend/ 与 frontend/ 目录为空。
  • 必须使用 --recurse-submodules 克隆或事后执行 git submodule update --init --recursive,否则无法获得可运行的前后端代码。
  • 以 VITE_ 开头的环境变量会被打包进浏览器公开代码,禁止在其中存放 API 密钥或私有令牌等真实机密。
  • AI 聊天功能依赖 Google Gemini API Key,需自行申请并配置,属于外部服务。
  • 联系表单邮件回退依赖 Gmail SMTP,需要 Gmail 账号并生成应用专用密码。
  • 联系表单的 WhatsApp 通知需配置 Meta WhatsApp Cloud API 的账号与访问令牌,属外部服务。
  • 展示 GitHub 贡献统计需提供 GitHub 用户名与访问令牌。
  • 展示 WakaTime 编码统计需提供 WakaTime API 密钥。
  • 启用 reCAPTCHA 需在 Google reCAPTCHA 后台申请站点密钥。
  • 部署配置面向 Vercel,使用该平台托管可能产生费用。
  • 模板自带的图片、简历 PDF、图标等演示素材需自行替换,其许可需单独核对。
  • 前后端为独立仓库,修改需先在各自子模块内提交推送,再在伞形仓库提交指针,否则改动不会生效。
查看原文依据

Permission is hereby granted, free of charge

许可证原文

This repository contains **no application code of its own** — it's an **umbrella (meta) repo** that stitches together two independent repositories via **Git submodules**:

README 原文

Because `backend/` and `frontend/` are Git submodules, clone recursively (or init them after cloning):

README 原文

Any variable starting with `VITE_` is embedded into the **public browser bundle**. Never put true secrets (API secrets, private tokens) in a `VITE_` variable.

README 原文

`GEMINI_API_KEY` | ✅ | Google Gemini key — https://aistudio.google.com/app/apikey

README 原文

`EMAIL_PASSWORD` | ✅ | Gmail **App Password** — https://myaccount.google.com/apppasswords

README 原文

**WhatsApp / Meta** | ⬜ | `APP_ID`, `APP_SECRET`, `RECIPIENT_WAID`, `VERSION`, `PHONE_NUMBER_ID`, `ACCESS_TOKEN`

README 原文

`GITHUB_USERNAME`, `GITHUB_TOKEN` — https://github.com/settings/tokens

README 原文

`WAKATIME_APP_SECRET` — https://wakatime.com/settings/api

README 原文

`VITE_RECAPTCHA_SITE_KEY` | ⬜ | reCAPTCHA **site** key — https://www.google.com/recaptcha/admin

README 原文

Both apps are configured for **Vercel** (`vercel.json` included in each folder).

README 原文

Also replace the assets in `frontend/public/`:

README 原文

`backend/` and `frontend/` each have their **own** `.git`, remote, branches and history.

README 原文
https://github.com/FIQTOR/fiqtor.com-create-your-own/blob/master/LICENSEhttps://github.com/FIQTOR/fiqtor.com-create-your-own/blob/master/README.md

DeepSeek 根据仓库许可证和 README 整理 ·2026-10-06。结果仅覆盖已读取资料,依赖、图片、字体及后续条款变更以各自原文为准。

项目核对记录与介绍依据

模板资料

来源平台
GitHub
资料与维护参考分
资料较完整 · 88 / 100
技术栈
Vite · React
网站类型
作品集
这个分数如何计算

本站按许可资料(最高30分)、演示链接(12分)、预览图(10分)、Star数(15分)、记录的维护时间(15分)、描述完整度(8分)、功能记录(5分)和分类资料(5分)计算。它不是代码、安全或性能测试,也不是 Google 评分;有链接不代表已实测可用。

这个项目需要核对什么

记录的仓库更新时间
2026-10-06
截图检查日期
2026-10-06

已收录演示链接。请用你的内容与屏幕尺寸检查;有演示不代表表单、认证或支付已经可用。

目录记录的主要技术栈是 Vite。请通过依赖清单和锁文件核对实际版本与托管要求。这些说明基于收录资料,不代表代码审计或部署实测。

查看具体选型方法 →

介绍的制作与核对状态

介绍由 AI 辅助整理目录与仓库资料。仓库作者不等于这篇介绍的审核者。

审核声明: [email protected] · 2026-10-07

查看当时使用的来源资料

SHA-256: f264918c430d9c5246eeef3c96c7ddd55016e274eba3dea41ddcd9d3d5d8eeb9

2026-10-06T14:44:57.222Z

来源文件 / 仓库
<div align="center">

# 🚀 Personal Portfolio Website

**A modern, full-stack personal portfolio template** — built with React 19 + Vite (frontend) and Node.js + Express (backend).

AI chatbot · Contact form (WhatsApp + Email) · GitHub & WakaTime stats · Certifications · Projects · Career timeline · Multi-language resume

> 📦 **This repo uses Git submodules.** `backend/` and `frontend/` are **separate Git repositories** referenced here as [submodules](#-submodules). See [Submodules](#-submodules) for how to clone and work with them.

</div>

<img src="/img/thumbnail.png" alt="thumbnail.png">

---

## 📑 Table of Contents

- [Features](#-features)
- [Tech Stack](#-tech-stack)
- [Project Structure](#-project-structure)
- [Submodules](#-submodules)
- [Prerequisites](#-prerequisites)
- [Installation](#-installation)
- [Configuration](#-configuration)
  - [Backend Environment Variables](#backend-environment-variables-backendenv)
  - [Frontend Environment Variables](#frontend-environment-variables-frontendenv)
  - [Customizing Your Portfolio Content](#customizing-your-portfolio-content)
- [Running the Project](#-running-the-project)
- [Available Scripts](#-available-scripts)
- [Testing & CI](#-testing--ci)
- [API Endpoints](#-api-endpoints)
- [Deployment](#-deployment)
- [Security Notes](#-security-notes)
- [Troubleshooting](#-troubleshooting)
- [License](#-license)

---

## ✨ Features

- 🤖 **AI Assistant** — Context-aware chatbot (Google Gemini / Groq) that answers questions about you. Supports **voice dictation** (Web Speech API → types straight into the composer), **file attachments** (Image / Video / Document via a type picker, max 4.5MB), streaming replies, and copy/regenerate actions.
- 📬 **Contact Form** — Sends notifications via WhatsApp Business API with automatic Email (SMTP) fallback.
- 📊 **Live Stats** — GitHub contribution graph and WakaTime coding activity.
- 🎨 **Modern UI** — Tailwind CSS v4, Framer Motion animations, dark/light theme, 3D & Lottie effects.
- 🗂️ **Portfolio Sections** — Projects, career timeline, certifications, skills, services.
- 💰 **Crypto Widget** — Static BTC/ETH/SOL prices served by the backend (no third-party API key to leak).
- 🔍 **SEO Ready** — Structured data (JSON-LD), OpenGraph, Twitter cards, sitemap, robots.
- 🌐 **Fully Configurable** — Personal/brand data lives in `frontend/src/config/Identity.ts` (code) and a few `VITE_*` env vars (no editing components needed).
- 📱 **Responsive** — Mobile-first design with a classic linktree page.
- 🧩 **Reusable Template** — Clean, easy-to-navigate structure; a "Portfolio template" link in the footer points to [`fiqtor.com-create-your-own`](https://github.com/FIQTOR/fiqtor.com-create-your-own).

---

## 🛠 Tech Stack

| Layer      | Technology                                                                                     |
| ---------- | ---------------------------------------------------------------------------------------------- |
| **Frontend** | React 19, TypeScript, Vite, Tailwind CSS v4, Framer Motion, React Router, react-helmet-async |
| **Backend**  | Node.js 20, Express, Axios, Nodemailer, Helmet, express-rate-limit                            |
| **AI**       | `@google/genai` (Gemini), `groq-sdk` (Groq)                                                     |
| **Services** | WhatsApp Cloud API, Gmail SMTP, GitHub GraphQL, WakaTime, reCAPTCHA |
| **Deploy**   | Vercel (frontend + backend)                                                                     |

---

## 📂 Project Structure

```
personal-website/               # 📦 umbrella repo (this one — holds only .gitmodules + docs)
├── .gitmodules                 # defines backend/ and frontend/ as submodules
├── README.md
├── AGENTS.md
│
├── backend/                    # 🔗 git submodule → FIQTOR/api.fiqtor
│   ├── index.js                # 🚀 server entry point
│   ├── src/
│   │   ├── config/             # CORS + static identity/crypto/social data
│   │   ├── controllers/        # ai, contact, github, wakatime controllers
│   │   ├── middleware/         # rate limiter, logger, error handler
│   │   ├── services/           # contact, github, wakatime, crypto, social services
│   │   └── routes/             # index (aggregator) + per-feature route files
│   ├── .env.example            # backend env template
│   └── vercel.json
│
└── frontend/                   # 🔗 git submodule → FIQTOR/fiqtor.com
    ├── public/                 # static assets (images, pdf, favicon)
    │   ├── img/                # project/company images
    │   └── pdf/                # resume files
    │   # sitemap.xml + robots.txt are GENERATED at build time (not stored here)
    ├── src/
    │   ├── components/         # shared UI components (Navbar, Footer, AIHelper…)
    │   ├── config/             # ⚙️ Identity.ts (code-based branding), Head, Metadata, Github/Wakatime
    │   ├── context/            # React contexts (theme, container, welcome)
    │   ├── data/               # 📝 YOUR CONTENT: projects, career, certificates…
    │   ├── layouts/            # layout wrappers
    │   ├── modules/            # feature modules — e.g. modules/home/{sections,components}, modules/contact/components
    │   ├── pages/              # route pages (file-based routing)
    │   ├── App.tsx             # app shell
    │   └── main.tsx            # entry point
    ├── index.html              # minimal shell (head injected at build from src/config/Head.ts)
    ├── vite.config.ts          # includes htmlHeadPlugin (head + sitemap + robots)
    └── .env.example            # frontend env template
```

> 💡 **Key idea:** Personal/brand data → **env vars** (`.env`). Portfolio content (projects, certificates, career) → **`src/data/*.ts`**. The `<head>` metadata is generated at build time from **`src/config/Head.ts`**.
>
> 🔗 **`backend/` and `frontend/` are Git submodules** — each is its own repository with its own history and remotes. This umbrella repo only pins *which commit* of each to use. See [Submodules](#-submodules).

---

## 🔗 Submodules

This repository contains **no application code of its own** — it's an **umbrella (meta) repo** that stitches together two independent repositories via **Git submodules**:

| Submodule    | Repository                              | Purpose                          |
| ------------ | --------------------------------------- | -------------------------------- |
| `backend/`   | `https://github.com/FIQTOR/api.fiqtor`  | Node.js + Express API            |
| `frontend/`  | `https://github.com/FIQTOR/fiqtor.com`  | React 19 + Vite web app          |

The mapping lives in [`.gitmodules`](.gitmodules):

```ini
[submodule "backend"]
	path = backend
	url = https://github.com/FIQTOR/api.fiqtor
[submodule "frontend"]
	path = frontend
	url = https://github.com/FIQTOR/fiqtor.com
```

**What this means in practice:**

- The umbrella repo stores only a **pointer** (commit SHA) to each submodule — not the files themselves.
- `backend/` and `frontend/` each have their **own** `.git`, remote, branches and history.
- Committing in the umbrella repo records the **current SHA** of each submodule (a "gitlink").
- CI/CD and Vercel import each submodule folder as its own project (see [Deployment](#-deployment)).

### Cloning with submodules

```bash
# Option A — clone everything in one go
git clone --recurse-submodules https://github.com/FIQTOR/fiqtor.com-create-your-own.git
cd fiqtor.com-create-your-own

# Option B — already cloned without submodules?
git submodule update --init --recursive
```

### Common submodule commands

```bash
# Check status / pinned commits
git submodule status

# Pull the latest commit for every submodule
git submodule update --remote --merge

# Update a single submodule
git submodule update --remote backend

# Run a git command inside a submodule
git -C frontend status

# Switch all submodules to the commit the umbrella repo pins
git submodule update --init --recursive
```

> ⚠️ **Edits inside a submodule are committed in the submodule's own repo first**, then the umbrella repo commits the new pointer. Pushing the umbrella repo alone does **not** push submodule commits.

---

## ✅ Prerequisites

Make sure you have installed:

- [Node.js](https://nodejs.org/) **v20.x or higher** (required by backend `engines`)
- [npm](https://www.npmjs.com/) (comes with Node) — or `pnpm` / `yarn`
- [Git](https://git-scm.com/)

---

## 📦 Installation

### 1. Clone the repository **with submodules**

Because `backend/` and `frontend/` are Git submodules, clone recursively (or init them after cloning):

```bash
# recommended — pulls the umbrella repo + both submodules
git clone --recurse-submodules https://github.com/<your-username>/personal-website.git
cd personal-website

# already cloned without them? fetch the submodule commits:
git submodule update --init --recursive
```

> ❓ If `backend/` or `frontend/` are empty after cloning, run `git submodule update --init --recursive`. See [Submodules](#-submodules).

### 2. Install backend dependencies

```bash
cd backend
npm install
```

### 3. Install frontend dependencies

```bash
cd ../frontend
npm install
```

### 4. Create environment files

Copy the example files and fill in your own values (see [Configuration](#-configuration)):

```bash
# Backend
cd ../backend
cp .env.example .env

# Frontend
cd ../frontend
cp .env.example .env
```

---

## ⚙️ Configuration

All configuration is done through environment variables. **Never commit your real `.env` files** — they are already excluded in `.gitignore`.

- **Backend** → `backend/.env`
- **Frontend** → `frontend/.env`

> ⚠️ Any variable starting with `VITE_` is embedded into the **public browser bundle**. Never put true secrets (API secrets, private tokens) in a `VITE_` variable. Branding/identity is code-based in `frontend/src/config/Identity.ts`; backend identity/social in `backend/src/config/identity.js`.

### Backend Environment Variables (`backend/.env`)

| Variable                | Required | Description                                                                 |
| ----------------------- | :------: | --------------------------------------------------------------------------- |
| `NODE_ENV`              |    ✅    | `development` or `production`                                               |
| `APP_PORT`              |    ✅    | Server port (default `4000`)                                                |
| `FRONTEND_HOST`         |    ✅    | Allowed CORS origin(s), e.g. `http://localhost:5173`                        |
| **AI**                  |          |                                                                             |
| `GEMINI_API_KEY`        |    ✅    | Google Gemini key — https://aistudio.google.com/app/apikey                  |
| `GROQ_API_KEY`          |    ⬜    | Groq key (optional) — https://console.groq.com/keys                        |
| **Identity / Social**   |    —     | **Not env** → code in `backend/src/config/identity.js` (brand, owner, company, social URLs) |
| **Email / SMTP**        |          |                                                                             |
| `EMAIL_HOST`            |    ✅    | SMTP host, e.g. `smtp.googlemail.com`                                       |
| `EMAIL_PORT`            |    ✅    | SMTP port, e.g. `465`                                                       |
| `EMAIL_USERNAME`        |    ✅    | SMTP username (your Gmail)                                                  |
| `EMAIL_PASSWORD`        |    ✅    | Gmail **App Password** — https://myaccount.google.com/apppasswords          |
| `EMAIL_RECIPIENT`       |    ✅    | Email that receives contact form submissions                                 |
| `EMAIL_BUSINESS`        |    ⬜    | Optional public business email                                              |
| **WhatsApp / Meta**     |    ⬜    | `APP_ID`, `APP_SECRET`, `RECIPIENT_WAID`, `VERSION`, `PHONE_NUMBER_ID`, `ACCESS_TOKEN` |
| **GitHub**              |    ⬜    | `GITHUB_USERNAME`, `GITHUB_TOKEN` — https://github.com/settings/tokens      |
| **WakaTime**            |    ⬜    | `WAKATIME_APP_SECRET` — https://wakatime.com/settings/api                   |
| `WAKATIME_TIMEOUT_MS`   |    ⬜    | Request timeout in ms (default `8000`)                                      |
| `WAKATIME_MAX_RETRIES`  |    ⬜    | Retries on transient errors (default `2`)                                   |
| `WAKATIME_CACHE_TTL_MS` |    ⬜    | Cache TTL in ms (default `300000` = 5 min)                                  |

> 💡 **AI identity & social URLs are code-based** — `backend/src/config/identity.js` (not secrets, so kept out of `.env`).

> 💡 **Crypto & social stats are static** — no env vars, no third-party API. Edit `backend/src/config/crypto.js` and `backend/src/config/social.js` and restart the server.

### Frontend Environment Variables (`frontend/.env`)

Only a few values remain in env — identity/branding moved into code (`frontend/src/config/Identity.ts`).

| Variable                  | Required | Description                                                       |
| ------------------------- | :------: | ----------------------------------------------------------------- |
| `VITE_DOMAIN`             |    ⬜    | Optional site-origin override (defaults to `SITE_ORIGIN` in `src/config/Identity.ts`) |
| `VITE_API_BASE_URL`       |    ✅    | Backend API URL, e.g. `http://localhost:4000/api`                 |
| `VITE_RECAPTCHA_SITE_KEY` |    ⬜    | reCAPTCHA **site** key — https://www.google.com/recaptcha/admin   |
| `VITE_ENABLE_AI`          |    ⬜    | `TRUE` / `FALSE` to toggle AI features                            |

> 📌 **Branding, owner identity, contact, social URLs, resume paths, company brand, site origin and GA ID** are code in [`frontend/src/config/Identity.ts`](frontend/src/config/Identity.ts). Integration usernames live in `src/config/Github.ts` and `src/config/Wakatime.ts` (social handles come from `SOCIAL_LINKS` in `Identity.ts`). **No `VITE_*` API keys exist.**

### Customizing Your Portfolio Content

Beyond env vars, edit these files in `frontend/src/data/` to replace the sample content with your own:

| File               | What it controls                                             |
| ------------------ | ----------------------------------------------------------- |
| `projects.ts`      | Project portfolio (title, image, description, links, tags)  |
| `career.ts`        | Work experience timeline                                    |
| `certificate.ts`   | Certificates, awards & credentials                          |
| `skills.ts`        | Skill icons & stack                                         |
| `services.ts`      | Services offered                                            |
| `menu.ts`          | Navigation menu items                                       |
| `social.ts`        | Social links (wired to `Identity.ts`)                       |
| `icons.tsx`        | Custom SVG icon components                                  |

> 📄 **`index.html` `<head>` metadata** (title, meta, OpenGraph, Twitter, JSON-LD, Google Analytics) is **generated at build time** from [`src/config/Head.ts`](frontend/src/config/Head.ts) via the `htmlHeadPlugin` in [`vite.config.ts`](frontend/vite.config.ts). You don't edit `index.html` directly — change the values in `Head.ts` (they read from the same `VITE_*` env vars), and the correct tags are injected automatically for both dev and production builds. This guarantees crawlers that don't run JavaScript still see accurate metadata.

> 🗺️ **`sitemap.xml` and `robots.txt` are also generated at build time** from `VITE_DOMAIN` (see `renderSitemap` / `renderRobots` in `Head.ts`) — no static files to maintain, no hardcoded domain in the repo. They are emitted to `dist/` on build and served automatically by the dev server.

> 🔑 **Google / Bing site-verification files** (e.g. `google*.html`, `BingSiteAuth.xml`) are intentionally **not** included — add your own verification file to `frontend/public/` after cloning if you need search-console verification.

Also replace the assets in `frontend/public/`:
- `img/projects/*` — project thumbnails
- `img/career/*` — company logos
- `img/certificate/*` — certificate images
- `pdf/*` — your resume files (update the paths in `frontend/src/config/Identity.ts` → `RESUME` to match your file names)
- `icon.webp`, `favicon.ico` — your logo/favicon

> `sitemap.xml` and `robots.txt` are **generated automatically** — do not add them to `public/`.

---

## ▶️ Running the Project

You need **two terminals** — one for backend, one for frontend.

**Terminal 1 — Backend:**
```bash
cd backend
npm run dev          # nodemon, auto-reload
# or
npm start            # production mode
```
Backend runs at **http://localhost:4000**

**Terminal 2 — Frontend:**
```bash
cd frontend
npm run dev
```
Frontend runs at **http://localhost:5173**

Open http://localhost:5173 in your browser. 🎉

---

## 📜 Available Scripts

### Frontend (`frontend/`)

| Command             | Description                                |
| ------------------- | ------------------------------------------ |
| `npm run dev`       | Start dev server with HMR                  |
| `npm run build`     | Type-check + build for production          |
| `npm run preview`   | Preview the production build locally       |
| `npm run lint`      | Run ESLint                                 |
| `npm test`          | Run unit tests (Vitest, run once)          |
| `npm run test:watch` | Run tests in watch mode                   |

### Backend (`backend/`)

| Command         | Description                          |
| --------------- | ------------------------------------ |
| `npm run dev`   | Start with nodemon (auto-reload)     |
| `npm start`     | Start the server                     |
| `npm test`      | Run unit tests (Vitest, run once)    |
| `npm run test:watch` | Run tests in watch mode         |

---

## 🧪 Testing & CI

Both repos ship with **Vitest** unit tests and a **GitHub Actions** CI workflow.

- **Frontend** (`frontend/`) — `src/**/*.{test,spec}.{ts,tsx}`, jsdom + Testing Library.
  Covers the Kanban store hook and pure utils.
- **Backend** (`backend/`) — `tests/**/*.test.js`, Node env + supertest.
  Covers routes/rate limiting (against `src/app.js`), the AI controller control
  flow, and the contact service WhatsApp → Email fallback.
- **CI** — each repo has `.github/workflows/ci.yml`. Frontend runs
  lint → typecheck → test → build; backend runs install → test. The frontend
  build injects placeholder `VITE_*` values so the head/sitemap plugin works.

> The Express app is built in `backend/src/app.js` (separate from `index.js`)
> so tests can exercise it via supertest without binding a port.

---

## 🔌 API Endpoints

All endpoints are served under `/api` (backend base URL), except `/health` and `/v1/public/*`.

| Method   | Endpoint                              | Description                          |
| -------- | ------------------------------------- | ------------------------------------ |
| `GET`    | `/health`                             | Health check (uptime)                |
| `POST`   | `/api/v1/ai/generate`                 | AI chat completion                   |
| `POST`   | `/api/v1/contact/send`                | Contact form (WhatsApp → Email)      |
| `GET`    | `/api/v1/github/contributions`        | GitHub contribution stats            |
| `GET`    | `/api/v1/wakatime`                    | WakaTime coding stats                |
| `GET`    | `/api/v1/crypto`                      | Static BTC/ETH/SOL prices            |
| `GET`    | `/api/v1/social/stats`                | Static TikTok/Instagram stats        |
| `GET`    | `/v1/public/stats`                    | Public social stats (same static data) |

---

## 🚢 Deployment

Both apps are configured for **Vercel** (`vercel.json` included in each folder).

> 🔗 **Submodule note:** `backend/` and `frontend/` are separate Git repos ([submodules](#-submodules)). Deploy each by importing its own repository (or the submodule path) as its own Vercel project — Vercel needs access to that repo, not just the umbrella one.

### Backend
1. Import the `backend/` folder (repo `FIQTOR/api.fiqtor`) as a Vercel project.
2. Add all backend env vars in **Settings → Environment Variables**.
3. Set `FRONTEND_HOST` to your production frontend URL (for CORS).
4. Deploy.

### Frontend
1. Import the `frontend/` folder as a Vercel project.
2. Add all `VITE_*` env vars in **Settings → Environment Variables**.
3. Set `VITE_DOMAIN` to your production domain and `VITE_API_BASE_URL` to your deployed backend URL.
4. Deploy.

> The frontend `vercel.json` rewrites all routes to `/index.html` (SPA routing), while keeping `sitemap.xml` and `robots.txt` intact.

---

## 🔒 Security Notes

- ❌ **Never commit `.env`** — real env files are ignored via `.gitignore`. Only `.env.example` is committed.
- 🔑 **Rotate secrets** if they were ever committed to git. Purge history with [`git filter-repo`](https://github.com/newren/git-filter-repo) or [BFG](https://rtyley.github.io/bfg-repo-cleaner/) and force-push.
- 🧾 Any `VITE_*` value is **public** (shipped to the browser). Keep secrets server-side only.
- 🔐 **No third-party API keys ship to the client.** Crypto prices and social stats are served by the backend as static data; integration usernames live in `src/config/*.ts`. If you ever switch to a live provider, proxy it through the backend — never expose the key via `VITE_*`.
- 🚫 **No secrets, tokens, personal data, or verification files are committed to this repo** — everything sensitive lives in `.env` (gitignored). The only `.env` tracked is `.env.example` (placeholders).
- 🛡️ The backend uses Helmet, HPP, rate limiting, and CORS — keep these enabled in production.
  - **Per-endpoint limits:** a broad global limiter, a lighter `statsLimiter` for the external-API read endpoints (GitHub/WakaTime), a `contactLimiter` for the form, and an `aiLimiter` **plus** a daily `aiDailyLimiter` quota on `/api/v1/ai/generate` (cost control). Limits live in `backend/src/middleware/rate-limiter.js`.
- 📧 **Email & DNS security (SPF, DKIM, DMARC, MTA-STS, HSTS, subdomains):** see [`docs/SECURITY-EMAIL-DNS.md`](docs/SECURITY-EMAIL-DNS.md) for the Cloudflare records and rollout steps.

---

## 🧰 Troubleshooting

| Problem                              | Fix                                                                                     |
| ------------------------------------ | --------------------------------------------------------------------------------------- |
| `Cannot find module './src/...'` | Ensure all controller/service/route files exist under `backend/src/`.            |
| CORS error in browser                | Set `FRONTEND_HOST` in `backend/.env` to your frontend origin.                          |
| AI not responding                    | Verify `GEMINI_API_KEY` (backend) and `VITE_API_BASE_URL` (frontend).                   |
| Contact form fails                   | Check `EMAIL_*` / WhatsApp vars; the backend falls back to email if WhatsApp fails.     |
| `.env` values not applying           | Restart the dev server (env vars are read on startup).                                  |
| GitHub/WakaTime stats empty          | Set the relevant tokens/usernames in `backend/.env`.                                    |
| WakaTime times out (ETIMEDOUT)       | Network/IPv6 issue — the service already retries + caches. Tune via `WAKATIME_TIMEOUT_MS`, `WAKATIME_MAX_RETRIES`, `WAKATIME_CACHE_TTL_MS` in `backend/.env`. |
| Sitemap/robots show wrong domain     | Set `VITE_DOMAIN` in `frontend/.env` and rebuild — they are generated from it.           |
| Horizontal scroll appears after load | Decorative full-bleed elements overflow the viewport. A global `overflow-x: clip` guard in `frontend/src/index.css` prevents it — avoid `w-screen` / `100vw` (use `w-full`). |
| Page width shifts / jumps on scroll-lock | `html { scrollbar-gutter: stable }` reserves the scrollbar space so locking scroll (e.g. AI panel open) never changes layout width. |
| `backend/` / `frontend/` folders empty after clone | They are submodules — run `git submodule update --init --recursive`.        |
| Submodule shows `-` (uninitialized)   | `git submodule update --init --recursive` (or `git submodule update --remote backend` to pull latest). |
| Submodule points to old commit        | `git submodule update --remote --merge`, then commit the new pointer in the umbrella repo. |
| Pushed umbrella repo but `backend` changes missing | Commit & push inside the submodule first (`git -C backend push`), then commit the pointer here. |

---

## 📄 License

ISC — free to use as a template. Attribution appreciated but not required.

---

<div align="center">

**Built with ❤️ — happy coding!**

</div>
反馈内容错误

看看同类模板

对比同技术栈与用途的模板,找到更适合的视觉与内容结构。