伞形仓库通过 Git 子模块组织前后端,frontend/ 与 backend/ 各自独立
fiqtor.com-create-your-own — Vite 作品集模板
作者 FIQTOR
基于 React 与 Vite 前端、Node.js 与 Express 后端的全栈个人作品集模板,仓库通过 Git 子模块组织前后端代码,提供 AI 聊天、联系表单、GitHub 与 WakaTime 统计、项目与证书展示等功能。
看看你的网站会是什么样
浏览真实页面截图,比较内容排版与手机效果。点击下方缩略图切换,放大查看整页细节。
目前有 1 张原始预览,可打开完整图片查看设计细节。
截图用于了解视觉与布局,交互效果请以实际项目为准。
值得关注的模板亮点
从已有项目资料中,快速了解它的特点。
AI 助手支持语音听写、文件附件、流式回复与复制/重新生成
联系表单通过 WhatsApp Business API 发送,失败时回退 SMTP 邮件
展示 GitHub 贡献图与 WakaTime 编码活动
包含项目、职业时间线、证书、技能、服务与多语言简历等板块
内容与品牌信息集中在配置文件和环境变量中,无需修改组件
模板详细介绍
这是一个用于搭建个人作品集网站的全栈模板。仓库本身是伞形(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_ 前缀的环境变量会进入浏览器公开包,不能存放密钥。
适合你的项目吗?
适用场景
- 搭建个人作品集网站,集中展示项目、职业经历与证书
- 在个人站点中集成 AI 助手,回答访客关于自己的问题
- 通过 WhatsApp 或邮件接收访客的联系表单提交
- 在个人主页展示 GitHub 贡献与 WakaTime 编码统计
- 作为学习 React + Vite 与 Node.js + Express 全栈结构的参考项目
适合人群
- 需要快速搭建个人作品集的前端或全栈开发者
- 希望展示 GitHub 与 WakaTime 数据的开发者
- 需要多语言简历与职业时间线展示的求职者
- 希望以 MIT 许可自由修改和分发代码的用户
上手与定制指南
结合仓库当前版本的说明,按下面的资料检查内容、配置与运行要求。
- 1
克隆时需使用 --recurse-submodules 或随后执行 git submodule update --init --recursive,否则 backend/ 与 frontend/ 目录为空
- 2
需分别进入 backend 和 frontend 目录执行 npm install,并各自复制 .env.example 为 .env 填写配置
- 3
后端需要 Node.js v20 或更高版本,并配置 Gemini API Key、SMTP 等环境变量
- 4
VITE_ 前缀的环境变量会进入浏览器公开包,不能放置真实密钥
- 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 原文
https://github.com/FIQTOR/fiqtor.com-create-your-own/blob/master/LICENSEhttps://github.com/FIQTOR/fiqtor.com-create-your-own/blob/master/README.md`backend/` and `frontend/` each have their **own** `.git`, remote, branches and history.
README 原文
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>
看看同类模板
对比同技术栈与用途的模板,找到更适合的视觉与内容结构。

Resumify
Free, privacy-first resume builder — 6 ATS-friendly templates, full customization, JSON import/export. No sign…
Vite · 作品集

showlit
✨ Showlit - A modern and clean template for creating a personal website
Vite · 作品集

folium
Professional developer portfolio boilerplate (React • TypeScript • Tailwind • Vite) with simple content custom…
Vite · 作品集

kurizu-discord
A Discord themed website built using React & Vite
Vite · 作品集

about-me-personal-portfolio-website-template
Simple Personal Website Template, built with bootstrap and vite js project.
Vite · 作品集

HackFolio
A high-performance, SEO-optimized hacker-themed portfolio website tailored for for ethical hackers, penetratio…
Vite · 作品集
