# fastify-scaffold **Repository Path**: nacker/fastify-scaffold ## Basic Information - **Project Name**: fastify-scaffold - **Description**: 基于 Fastify + TypeScript + TypeORM + Redis 的生产级脚手架,提供高可用架构支持。 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2026-04-04 - **Last Updated**: 2026-04-29 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Fastify 生产级脚手架 基于 Fastify + TypeScript + TypeORM + Redis 的生产级脚手架,采用 App/Admin 双端架构,提供高可用架构支持。 ## 核心特性 - **App/Admin 双端架构** - 用户端与后台管理端分离,独立认证与路由 - **Fastify 适配器** - 高性能 Node.js Web 框架 - **统一全局拦截器** - 请求/响应拦截和错误处理 - **结构化日志** - 基于 Pino 的多通道日志(控制台 + 文件) - **响应格式化** - 统一的 API 响应格式 - **环境配置管理** - 支持 .env 配置,路径安全解析 - **JWT 双端鉴权** - App 与 Admin 独立 Token 体系与白名单 - **路由白名单** - 灵活配置免认证接口 - **跨域支持** - CORS 配置 - **请求参数校验** - 基于 class-validator + class-transformer - **TypeORM + MySQL** - 完整的数据库支持,UUID 主键 + 软删除 - **Redis 缓存** - 基于 ioredis 的高性能缓存支持 - **Swagger 分组文档** - App/Admin/System 三组自动生成 API 文档 - **优雅停机** - SIGINT/SIGTERM 信号优雅关闭服务 - **热重载开发** - tsx watch 自动重启 - **TypeScript** - 完整的类型支持与路径别名 ## 技术栈 | 类别 | 技术 | | ------ | ----------------------------------- | | 框架 | Fastify 4.x | | 语言 | TypeScript 5.x | | 数据库 | MySQL + TypeORM 0.3.x | | 缓存 | Redis + ioredis | | 认证 | JWT (@fastify/jwt) - App/Admin 双端 | | 验证 | class-validator + class-transformer | | 文档 | Swagger/OpenAPI (@fastify/swagger) | | 日志 | Pino + pino-pretty | | 测试 | Jest + ts-jest | ## 目录结构 ``` src/ ├── index.ts # 应用入口 ├── admin/ # Admin 后台管理模块 │ ├── controllers/ # 控制器(认证/个人信息/用户管理/仪表盘) │ ├── dto/ # 数据传输对象 │ ├── services/ # 业务逻辑 │ ├── vo/ # 视图对象 │ └── routes.ts # 路由注册 ├── app/ # App 用户端模块 │ ├── controllers/ # 控制器(认证/用户) │ ├── dto/ # 数据传输对象 │ ├── services/ # 业务逻辑 │ └── routes.ts # 路由注册 ├── common/ # 公共模块 │ ├── dto/ # 通用 DTO(分页等) │ ├── vo/ # 通用 VO │ ├── constants.ts # 常量定义 │ └── types.ts # 类型定义 ├── config/ # 配置模块 │ ├── index.ts # 配置加载(.env 路径安全解析) │ ├── database.ts # 数据库配置 │ ├── redis.ts # Redis 配置 │ ├── jwt.ts # JWT 认证(App/Admin 双端验证) │ ├── cors.ts # CORS 配置 │ ├── swagger.ts # Swagger 分组配置 │ └── whitelist.ts # 路由白名单(App/Admin 分离) ├── controllers/ # 全局控制器 │ └── health.controller.ts # 健康检查 ├── services/ # 全局服务 │ ├── auth.service.ts # 认证服务 │ └── user.service.ts # 用户服务 ├── shared/ # 共享模块 │ ├── entities/ # 数据库实体 │ │ ├── base.entity.ts # 基础实体(UUID/时间戳/软删除) │ │ ├── admin.entity.ts # 管理员实体 │ │ └── user.entity.ts # 用户实体 │ └── middleware/ # 中间件 │ ├── logger.ts # 请求日志 │ └── response.ts # 统一响应格式化 + 错误处理 ├── routes/ # 路由入口 │ └── index.ts # 路由注册(App/Admin/System) └── utils/ # 工具类 ├── create-admin.ts # 创建管理员脚本 ├── logger.ts # Pino 日志实例 ├── password.ts # 密码加密/验证(bcryptjs) ├── errors.ts # AppError 异常类 ├── validator.ts # DTO 校验工具 ├── gracefulShutdown.ts # 优雅停机 ├── date.ts # 日期工具 ├── string.ts # 字符串工具 └── index.ts # 工具统一导出 ``` ## 快速开始 ### 安装依赖 ```bash pnpm install ``` ### 配置环境变量 ```bash cp .env.example .env ``` ### 环境变量说明 | 变量 | 说明 | 默认值 | | -------------- | ----------- | ---------------- | | NODE_ENV | 环境 | development | | PORT | 端口 | 3000 | | HOST | 监听地址 | 0.0.0.0 | | API_PREFIX | API前缀 | /api/v1 | | JWT_SECRET | JWT密钥 | - | | JWT_EXPIRES_IN | Token有效期 | 7d | | DB_HOST | 数据库地址 | localhost | | DB_PORT | 数据库端口 | 3306 | | DB_USERNAME | 数据库用户名 | root | | DB_PASSWORD | 数据库密码 | - | | DB_DATABASE | 数据库名 | fastify_scaffold | | DB_SYNCHRONIZE | TypeORM自动同步 | false | | DB_LOGGING | SQL日志输出 | true | | REDIS_HOST | Redis地址 | localhost | | REDIS_PORT | Redis端口 | 6379 | | REDIS_PASSWORD | Redis密码 | - | | REDIS_DB | Redis数据库 | 0 | | LOG_LEVEL | 日志级别 | info | | LOG_DIR | 日志目录 | ./logs | ### 启动服务 ```bash pnpm dev # 开发模式(热重载) pnpm build # 构建 pnpm start # 生产模式 ``` ## 数据库初始化 项目提供 `schema.sql` 文件用于 MySQL 5.7+ 数据库初始化,支持 `utf8mb4` 字符集。 ### 快速导入 ```bash mysql -u root -p < schema.sql ``` 或在 MySQL 客户端中执行: ```sql source schema.sql; ``` ### 数据库表结构 | 表名 | 说明 | 字符集 | |------|------|--------| | `users` | 用户表 | utf8mb4 | | `admins` | 管理员表 | utf8mb4 | ### users 表字段 | 字段 | 类型 | 约束 | 说明 | |------|------|------|------| | `id` | `varchar(36)` | 主键 | UUID 唯一标识 | | `username` | `varchar(100)` | 非空、唯一 | 用户名 | | `password` | `varchar(255)` | 非空 | 加密存储的密码 | | `email` | `varchar(100)` | 可空 | 邮箱地址 | | `phone` | `varchar(20)` | 可空 | 手机号 | | `nickname` | `varchar(100)` | 可空 | 用户昵称 | | `status` | `tinyint` | 默认 1 | 状态:1-正常,0-禁用 | | `avatar` | `text` | 可空 | 头像地址 | | `created_at` | `datetime(6)` | 自动填充 | 创建时间 | | `updated_at` | `datetime(6)` | 自动更新 | 更新时间 | | `is_deleted` | `tinyint` | 默认 0 | 软删除标记:0-未删除,1-已删除 | ### admins 表字段 | 字段 | 类型 | 约束 | 说明 | |------|------|------|------| | `id` | `varchar(36)` | 主键 | UUID 唯一标识 | | `username` | `varchar(50)` | 非空、唯一 | 用户名 | | `password` | `varchar(255)` | 非空 | 加密存储的密码 | | `nickname` | `varchar(100)` | 可空 | 昵称 | | `avatar` | `text` | 可空 | 头像地址 | | `status` | `tinyint` | 默认 1 | 状态:1-正常,0-禁用 | | `last_login_at` | `datetime(6)` | 可空 | 最后登录时间 | | `last_login_ip` | `varchar(50)` | 可空 | 最后登录IP | | `created_at` | `datetime(6)` | 自动填充 | 创建时间 | | `updated_at` | `datetime(6)` | 自动更新 | 更新时间 | | `is_deleted` | `tinyint` | 默认 0 | 软删除标记:0-未删除,1-已删除 | ### 索引说明 **users 表** | 索引名 | 字段 | 类型 | 用途 | |--------|------|------|------| | `PRIMARY` | `id` | 主键 | 唯一标识 | | `uk_username` | `username` | 唯一索引 | 用户名唯一约束 | | `idx_email` | `email` | 普通索引 | 邮箱查询 | | `idx_phone` | `phone` | 普通索引 | 手机号查询 | | `idx_status` | `status` | 普通索引 | 状态筛选 | | `idx_is_deleted` | `is_deleted` | 普通索引 | 软删除筛选 | **admins 表** | 索引名 | 字段 | 类型 | 用途 | |--------|------|------|------| | `PRIMARY` | `id` | 主键 | 唯一标识 | | `uk_username` | `username` | 唯一索引 | 用户名唯一约束 | | `idx_status` | `status` | 普通索引 | 状态筛选 | | `idx_is_deleted` | `is_deleted` | 普通索引 | 软删除筛选 | > **注意**:生产环境务必将 `DB_SYNCHRONIZE` 设为 `false`,使用 `schema.sql` 或迁移脚本管理数据库结构。 ## 创建管理员 项目提供命令行脚本用于创建初始管理员账号: ```bash # 默认创建(用户名: admin,密码: admin123,昵称: 超级管理员) pnpm create-admin # 自定义创建 pnpm create-admin <用户名> <密码> <昵称> ``` 示例: ```bash pnpm create-admin myadmin mypassword123 "运维管理员" ``` ## API 接口 开发环境访问 Swagger 文档:`http://localhost:3000/docs` Swagger 文档按功能分组为 **App**、**Admin**、**System** 三大板块,支持接口排序和在线调试。 ### System 接口 | 方法 | 路径 | 说明 | 认证 | |------|------|------|------| | GET | /api/v1/health | 健康检查 | 否 | ### App 接口(用户端) | 方法 | 路径 | 说明 | 认证 | |------|------|------|------| | POST | /api/v1/auth/login | 手机号登录/注册 | 否 | | GET | /api/v1/users/me | 获取当前用户信息 | 是 | | PUT | /api/v1/users/me | 更新当前用户信息 | 是 | | DELETE | /api/v1/users/me | 注销当前用户 | 是 | ### Admin 接口(后台管理) | 方法 | 路径 | 说明 | 认证 | |------|------|------|------| | POST | /api/v1/admin/auth/login | 管理员登录 | 否 | | POST | /api/v1/admin/auth/logout | 管理员登出 | 是 | | POST | /api/v1/admin/auth/refresh | 刷新Token | 否 | | GET | /api/v1/admin/me | 获取当前管理员信息 | 是 | | PUT | /api/v1/admin/me | 更新当前管理员信息 | 是 | | PUT | /api/v1/admin/me/password | 修改密码 | 是 | | GET | /api/v1/admin/users | 获取用户列表(分页/搜索) | 是 | | GET | /api/v1/admin/users/:id | 获取用户详情 | 是 | | PUT | /api/v1/admin/users/:id/status | 更新用户状态(启用/禁用) | 是 | | DELETE | /api/v1/admin/users/:id | 删除用户(软删除) | 是 | | GET | /api/v1/admin/dashboard/stats | 获取仪表盘统计数据 | 是 | ### 请求示例 **App 登录/注册** ```bash curl -X POST http://localhost:3000/api/v1/auth/login \ -H "Content-Type: application/json" \ -d '{"phone": "13800138000", "password": "123456"}' ``` **Admin 登录** ```bash curl -X POST http://localhost:3000/api/v1/admin/auth/login \ -H "Content-Type: application/json" \ -d '{"username": "admin", "password": "admin123"}' ``` **认证请求(App)** ```bash curl http://localhost:3000/api/v1/users/me \ -H "Authorization: Bearer " ``` **认证请求(Admin)** ```bash curl http://localhost:3000/api/v1/admin/me \ -H "Authorization: Bearer " ``` ### 响应示例 ```json { "code": 200, "message": "Success", "data": { "token": "eyJhbGciOiJIUzI1NiIs..." }, "timestamp": 1712234567890, "requestId": "uuid" } ``` ## 架构设计 ### 请求处理流程 ``` 请求 → 日志记录 → 响应格式化 → JWT白名单验证 → 路由匹配 → Controller → Service → 返回 ``` ### App/Admin 双端架构 ``` ┌─────────────────────────────────────┐ │ Fastify Server │ └──────────────┬──────────────────────┘ │ ┌──────────────┴──────────────┐ │ registerRoutes │ └───┬──────────┬──────────┬───┘ │ │ │ ┌──────┴───┐ ┌────┴────┐ ┌───┴──────┐ │ System │ │ App │ │ Admin │ │ /api/v1 │ │/api/v1 │ │/api/v1/ │ │ │ │ │ │ admin │ ├──────────┤ ├─────────┤ ├──────────┤ │ health │ │ auth │ │ auth │ │ │ │ users │ │ me │ │ │ │ │ │ users │ │ │ │ │ │dashboard │ └──────────┘ └─────────┘ └──────────┘ │ │ │ │ ┌─────┴─────┐ ┌────┴────┐ │ │authenticate│ │authenticate│ │ │ App │ │ Admin │ │ └───────────┘ └──────────┘ │ │ │ │ JwtPayload AdminJwtPayload │ (type:app) (type:admin) ``` ### 统一响应格式 ```typescript { code: number, // 业务响应码 message: string, // 响应消息 data: any, // 响应数据(错误时为 null) timestamp: number, // 时间戳 requestId?: string // 请求ID } ``` ### 响应码说明 | 码值 | 说明 | | ---- | ---------------- | | 200 | 成功 | | 400 | 参数校验失败 | | 401 | 未登录/Token失效 | | 403 | 无权限 | | 404 | 资源不存在 | | 500 | 服务器错误 | ### JWT 双端认证 App 和 Admin 使用同一 JWT Secret 但通过 `type` 字段区分身份,互不越权: | 端 | Payload 类型 | type 字段 | 认证装饰器 | 白名单 | |----|-------------|-----------|-----------|--------| | App | `JwtPayload` | `app` | `fastify.authenticateApp` | `appWhitelist` | | Admin | `AdminJwtPayload` | `admin` | `fastify.authenticateAdmin` | `adminWhitelist` | ### 认证白名单 在 `src/config/whitelist.ts` 分别配置 App 和 Admin 免认证路由: ```typescript export const appWhitelist: string[] = [ '/api/v1/auth/login', '/api/v1/auth/register', '/api/v1/health', '/api/v1/documentation/*', ] export const adminWhitelist: string[] = [ '/api/v1/admin/auth/login', '/api/v1/admin/auth/refresh', '/api/v1/health', '/api/v1/documentation/*', ] ``` 白名单支持 `*` 通配符匹配。 ### 错误处理 ```typescript import { AppError } from '@/utils/errors' import { RESPONSE_CODE } from '@/common/constants' // 抛出业务异常,自动映射 HTTP 状态码 throw new AppError(RESPONSE_CODE.NOT_FOUND, '用户不存在') ``` ## 开发规范 ### 路径别名 ```typescript import { config } from '@/config' import { UserService } from '@/services/user.service' import { User } from '@/shared/entities/user.entity' import { AdminAuthService } from '@/admin/services/auth.service' ``` | 别名 | 路径 | |------|------| | `@/*` | `src/*` | | `@config` | `src/config` | | `@controllers` | `src/controllers` | | `@services` | `src/services` | | `@entities` | `src/shared/entities` | | `@middleware` | `src/shared/middleware` | | `@common` | `src/common` | | `@utils` | `src/utils` | | `@routes` | `src/routes` | ### 创建实体 ```typescript import { Entity, Column } from 'typeorm' import { BaseEntity } from '@/shared/entities/base.entity' @Entity('table_name') export class YourEntity extends BaseEntity { @Column({ comment: '字段说明' }) fieldName: string } ``` `BaseEntity` 内置 `id`(UUID主键)、`createdAt`、`updatedAt`、`isDeleted`(软删除)字段。 ### 创建 DTO DTO 按模块归属放置: - App 端:`src/app/dto/` - Admin 端:`src/admin/dto/` - 通用:`src/common/dto/` ```typescript import { IsString, MinLength, IsOptional } from 'class-validator' export class YourDto { @IsString({ message: '字段必须是字符串' }) @MinLength(1, { message: '字段不能为空' }) field!: string @IsOptional() @IsString() optionalField?: string } ``` ### 创建控制器 控制器按模块归属放置: - App 端:`src/app/controllers/` - Admin 端:`src/admin/controllers/` - 全局:`src/controllers/` ```typescript import { FastifyInstance } from 'fastify' export async function yourController(fastify: FastifyInstance) { fastify.get( '/route', { onRequest: [fastify.authenticateApp], // 或 fastify.authenticateAdmin schema: { tags: ['App - 分组名'], // 或 'Admin - 分组名' / 'System - 分组名' summary: '接口说明', security: [{ bearerAuth: [] }], }, }, async (request, reply) => { // ... } ) } ``` ### Swagger Tag 命名规范 | 前缀 | 用途 | 示例 | |------|------|------| | `App - ` | 用户端接口 | `App - 认证`、`App - 用户` | | `Admin - ` | 后台管理接口 | `Admin - 认证`、`Admin - 用户管理` | | `System - ` | 系统级接口 | `System - 健康检查` | ## 常用命令 ```bash pnpm dev # 开发模式(热重载) pnpm build # 构建 pnpm start # 生产模式 pnpm lint # 代码检查 pnpm lint:fix # 检查并修复 pnpm format # 格式化 pnpm test # 测试 pnpm test:watch # 测试(监听模式) pnpm create-admin # 创建管理员账号 ``` ## 生产环境部署 ### 环境准备 | 依赖 | 版本要求 | 说明 | |------|----------|------| | Node.js | >= 18.0.0 | 运行环境 | | MySQL | >= 5.7 | 主数据库 | | Redis | >= 5.0 | 缓存/会话 | | PM2 | 最新版 | 进程管理(推荐) | ### 1. 服务器初始化 ```bash # 安装 Node.js 18+ (使用 nvm) curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash source ~/.bashrc nvm install 18 nvm use 18 # 安装 PM2 npm install -g pm2 # 安装 pnpm npm install -g pnpm ``` ### 2. 数据库准备 ```bash # 登录 MySQL mysql -u root -p # 创建数据库(如未创建) CREATE DATABASE IF NOT EXISTS fastify_scaffold CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; # 导入表结构 source /path/to/project/schema.sql; # 创建专用账户(推荐) CREATE USER 'fastify_user'@'localhost' IDENTIFIED BY 'your-strong-password'; GRANT ALL PRIVILEGES ON fastify_scaffold.* TO 'fastify_user'@'localhost'; FLUSH PRIVILEGES; ``` ### 3. 项目部署 ```bash # 克隆代码 git clone /opt/fastify-scaffold cd /opt/fastify-scaffold # 安装依赖并构建 pnpm install pnpm build # 仅保留生产依赖 pnpm prune --production ``` ### 4. 环境变量配置 创建 `/opt/fastify-scaffold/.env` 文件: ```bash # 基础配置 NODE_ENV=production PORT=3000 HOST=0.0.0.0 API_PREFIX=/api/v1 # JWT 配置(务必使用强密钥) JWT_SECRET=your-super-secret-jwt-key-min-32-characters-long JWT_EXPIRES_IN=7d # 数据库配置 DB_HOST=localhost DB_PORT=3306 DB_USERNAME=fastify_user DB_PASSWORD=your-strong-password DB_DATABASE=fastify_scaffold DB_SYNCHRONIZE=false DB_LOGGING=false # Redis 配置 REDIS_HOST=localhost REDIS_PORT=6379 REDIS_PASSWORD=your-redis-password REDIS_DB=0 # 日志配置 LOG_LEVEL=info LOG_DIR=./logs ``` > **安全提示**: > - `JWT_SECRET` 必须替换为随机生成的强密钥(建议 32 位以上) > - 生产环境务必设置 `DB_SYNCHRONIZE=false` > - 生产环境建议关闭 `DB_LOGGING=false` > - 使用专用数据库账户,避免 root ### 5. 创建初始管理员 ```bash pnpm create-admin <用户名> <密码> <昵称> ``` ### 6. PM2 进程管理 创建 `ecosystem.config.js`: ```javascript module.exports = { apps: [ { name: 'fastify-scaffold', script: './dist/index.js', instances: 'max', // 根据 CPU 核心数启动多实例 exec_mode: 'cluster', // 集群模式 max_memory_restart: '500M', env: { NODE_ENV: 'production', }, log_date_format: 'YYYY-MM-DD HH:mm:ss Z', error_file: './logs/err.log', out_file: './logs/out.log', merge_logs: true, autorestart: true, watch: false, min_uptime: '10s', max_restarts: 5, }, ], } ``` 启动服务: ```bash # 启动 pm2 start ecosystem.config.js # 查看状态 pm2 status pm2 logs fastify-scaffold # 重启 pm2 restart fastify-scaffold # 停止 pm2 stop fastify-scaffold # 保存配置(开机自启) pm2 save pm2 startup ``` ### 7. Nginx 反向代理(推荐) ```nginx upstream fastify_backend { server 127.0.0.1:3000; keepalive 64; } server { listen 80; server_name api.yourdomain.com; # 强制 HTTPS return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; server_name api.yourdomain.com; ssl_certificate /path/to/cert.pem; ssl_certificate_key /path/to/key.pem; ssl_protocols TLSv1.2 TLSv1.3; location / { proxy_pass http://fastify_backend; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_cache_bypass $http_upgrade; proxy_read_timeout 60s; } } ``` ### 8. 健康检查 ```bash # 本地检查 curl http://localhost:3000/api/v1/health # 通过 Nginx 检查 curl https://api.yourdomain.com/api/v1/health ``` 预期响应: ```json { "code": 200, "message": "Success", "data": {}, "timestamp": 1712234567890, "requestId": "uuid" } ``` ### 9. 日志管理 ```bash # 查看实时日志 pm2 logs fastify-scaffold # 查看日志文件 ls -la ./logs/ # 日志轮转(配合 logrotate) # /etc/logrotate.d/fastify-scaffold /opt/fastify-scaffold/logs/*.log { daily rotate 14 compress delaycompress missingok notifempty create 0644 www-data www-data sharedscripts postrotate pm2 reloadLogs endscript } ``` ### 10. 更新部署 ```bash cd /opt/fastify-scaffold # 拉取最新代码 git pull origin master # 安装依赖并构建 pnpm install pnpm build # 重启服务 pm2 restart fastify-scaffold # 验证 pm2 logs ``` ## 注意事项 1. 生产环境务必设置 `DB_SYNCHRONIZE=false`,使用 `schema.sql` 或迁移脚本管理数据库结构 2. 生产环境必须更换 `JWT_SECRET` 为强密钥(建议 32 位以上随机字符串) 3. 生产环境建议关闭 `DB_LOGGING=false`,避免日志过大 4. 实体默认使用 `isDeleted` 字段实现软删除,物理删除需谨慎 5. 日志文件存储在 `./logs` 目录,建议配置 logrotate 防止磁盘占满 6. 已实现 SIGINT/SIGTERM 信号优雅停机,确保 PM2 重启时连接正常关闭 7. 建议配置 Nginx 反向代理和 HTTPS,不要直接暴露 Node.js 端口到公网 8. App 和 Admin 使用独立 JWT 认证流程,Token 互不通用,防止越权访问 9. 使用 `pnpm create-admin` 创建管理员而非手动插入数据库,确保密码正确加密 ## 许可证 MIT