Files
doodle-store/docs/v3.5/Server基础设置.md

78 lines
8.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Doodle Store Server 基础设置
> 更新日期:2026-10-15。已实现基础配置、日志、中间件、Swagger、微信登录、Prisma 账户查询与 JWT 守卫。
后续业务接入见 [用户鉴权与登录设计](./用户鉴权与登录设计.md);服务端登录模式和 reason 已接入,小程序恢复及 Web Cookie 会话仍是目标设计。当前接口与验证说明见 [服务端登录接入说明](./服务端登录接入说明.md)。
参考 `atlas-seller-kit/apps/api` 的应用工厂、结构化日志、requestId、请求体限制、默认鉴权与错误处理。保持本项目 NestJS 11 和 `/api/v1` 契约,不复制卖家工作台的多租户、Redis、对象存储或业务模块。
## 1. 启动与环境变量
从 `doodle-store` 根目录执行 `pnpm dev`。服务只读取 `apps/server/.env`,不向上搜索根目录的 Compose `.env`;部署环境变量优先于 `.env`。应用工厂不自行监听端口,main 负责读取配置和启动,测试使用临时端口。
开发命令通过 `apps/server/scripts/dev.mjs` 启动 Nest watch,在收到实际监听成功的通知后,输出 `Swagger: <完整地址>` 并使用系统默认浏览器打开一次。热更新不重复打开;`SWAGGER_ENABLED=false` 时只提示文档已关闭。链接使用实际监听 IP/端口,通配地址转换为本机地址。通知独立于日志级别;打开浏览器失败只提示手动访问,不终止服务。`pnpm start` 保留服务端启动行为,不自动打开浏览器,`server_started` 结构化日志增加 `swagger_url`。
新环境先复制 `apps/server/.env.example` 为 `apps/server/.env`,设置 JWT_SECRET。生成随机密钥的示例命令:
```bash
node -e "console.log(require('node:crypto').randomBytes(32).toString('hex'))"
```
真实密钥仅写入受控环境配置,不提交仓库。当前本机已生成独立开发密钥;测试使用测试专用密钥,生产必须另行生成。
| 配置 | 默认 / 要求 |
| ------------------------- | ----------------------------------------------------------------- |
| NODE_ENV | development;允许 development/test/production |
| HOST / PORT | 127.0.0.1 / 8800;端口须为 1~65535 整数 |
| LOG_LEVEL | info;允许 error/warn/info/debug |
| API_BODY_LIMIT_BYTES | 1048576,即 1 MiB;允许 1 KiB~10 MiB |
| SWAGGER_ENABLED | 未设置时开发/测试 true,生产 false;生产不要照搬开发示例中的 true |
| CORS_ORIGINS | 空列表;逗号分隔的完整 HTTP(S) origin,不接受 * 或路径 |
| TRUSTED_PROXY_CIDRS | 空列表;按部署显式设置代理 IP/CIDR,不使用 true 或跳数 |
| JWT_SECRET | 必填,至少 32 字节;拒绝空值、过短或 change-me 占位值 |
| JWT_ISSUER / JWT_AUDIENCE | doodle-store / doodle-mini |
| JWT_EXPIRES_IN_SECONDS | 86400,即 24 小时;允许 60~604800 秒 |
| WECHAT_MINI_APP_ID | 小程序 AppID;必填,必须与 AppSecret 成对提供 |
| WECHAT_MINI_APP_SECRET | 必填;无默认密钥,仅服务端持有 |
微信配置写入 `apps/server/.env`,不写根目录 Compose `.env`。服务端通过 `ServerConfig.wechatMiniAppId/wechatMiniAppSecret` 读取;两项去除首尾空白后校验非空且不含内部空白,错误不回显实际值。登录时使用 code2Session 验证身份,启动校验不代表已验证微信凭据真实有效。AppSecret 与 JWT_SECRET 是独立密钥,不得混用、下发或输出到日志。
DATABASE_URL 现在必填,必须为 PostgreSQL URL 且使用 public schema;Prisma 单应用连接池最多 5 条连接,连接超时 2 秒、语句超时 5 秒,应用关闭时释放。GET /health 只证明 HTTP 进程可用,GET /health/ready 检查数据库连通性;七牛待接入。
## 2. 请求处理
- 全部控制器使用 `/api/v1` 前缀,公开健康检查为 `GET /api/v1/health`。
- 创建 Nest 应用时设置 `bodyParser: false`,再显式安装 JSON 和 urlencoded 解析器;共同使用字节限制,urlencoded 使用 extended=false。
- requestId 在解析请求体之前生成;符合 `[A-Za-z0-9_-]{8,128}` 的 X-Request-Id 可沿用,否则生成 req_UUID。所有响应携带 X-Request-Id。
- 请求完成记录 method、path、状态、耗时及 requestId,不记录 query、请求体、Authorization、Cookie。JSON 日志递归脱敏密码、token、微信身份及其他敏感字段。
- Helmet 安全头与隐藏 X-Powered-By。Swagger 开启时放宽文档所需 CSP;Swagger 关闭时使用默认 CSP。
- CORS 使用精确 origin 白名单,无 Cookie 凭证模式;允许 Bearer Authorization,并向浏览器暴露 X-Request-Id。无 origin 的小程序/服务端请求可用,CORS 不代替身份校验。
- DTO 使用 ValidationPipe:transform、whitelist、forbidNonWhitelisted;不隐式把数字字符串转换成数字,不在错误里返回原始对象或值。
- 错误统一为 `{code: HTTP状态码, message: 安全提示, requestId}`,保留 HTTP 状态;JSON 格式错误 400、超限 413、未登录 401、无权限 403、未预期错误 500。可信业务错误新增有限 reason,限流错误携带 Retry-After;成功响应沿用 `{code:0,message,data}`,不重复包装健康检查。
## 3. Swagger
开发默认地址:`http://127.0.0.1:8800/api-docs`;OpenAPI JSON:`/api-docs-json`。Swagger 反映实际注册路由,不展示计划中尚未实现的业务接口。
使用 access-token Bearer JWT 安全定义,健康检查声明为公开。Swagger 不持久保存浏览器 token。生产默认关闭;如部署时显式开启,应通过部署入口限制访问。Swagger 是直接注册的文档路由,不由控制器 JWT 守卫保护。
## 4. JWT 与账户检查
`JwtTokenService` 仅供可信服务端业务签发 token,没有公开的测试登录或任意用户 token 签发接口。使用 HS256,签发 sub=userId、authVersion、iat、exp,并验证 algorithm、issuer、audience、有效期和身份字段类型。
`AccessTokenGuard` 为全局守卫,路由默认需要登录。公开接口使用 `@Public()`,业务代码用 `@CurrentUser()` 获取已核对的身份;管理接口使用 `@Roles('admin')`,角色从当前账户取得,不读取客户端或 token 自报角色。
验签后通过 ACCOUNT_READER 查询账户,要求用户存在、active、userId 一致且 authVersion 匹配。已注销、禁用或版本失效返回 401;不满足角色返回 403。数据库查询失败不降级为“JWT 有效即可通过”。
ACCOUNT_READER 现已接入真实 Prisma 查询;数据库失败返回 503/DATABASE_UNAVAILABLE,不降级放行。POST /auth/wechat/login 接入微信 code2Session、start/resume、唯一冲突处理及行锁;GET /me 返回本人摘要。资料修改、注销及其他业务 API 待实现。测试中的账户读取替身、微信 HTTP 替身和 probe 路由只在测试中使用。
## 5. 验证
使用 `pnpm check` 验证格式、ESLint、类型、构建与测试。集成测试覆盖健康检查、前缀、Swagger 开关与 Bearer 定义、CORS、安全头、requestId、JSON/urlencoded、超限和 DTO 校验、日志脱敏、JWT 伪造/过期/错误签名及 claims、账户禁用/缺失/版本失效、服务端角色校验和数据库不可用时的拒绝放行。
2026-10-13:`pnpm check` 全部通过,12 项测试通过。另使用构建后的 `dist/main.js` 启动真实进程,核对服务端 `.env` 加载、健康检查 HTTP 200、OpenAPI JSON HTTP 200 和 X-Request-Id;检查进程已关闭,未修改开发数据库或云服务器。
2026-10-15:接入微信静默登录与真实账户读取后,`pnpm check` 的 20 项常规测试和独立 `pnpm test:db` 的 8 项检查通过;构建产物真实启动/数据库连接通过。详细边界与记录见 [服务端登录接入说明](./服务端登录接入说明.md)。
基于 [NestJS Swagger 文档](https://docs.nestjs.com/openapi/introduction) 的运行时文档生成方式及 [ValidationPipe 文档](https://docs.nestjs.com/techniques/validation) 的 DTO 校验机制实现;依赖锁定兼容当前 NestJS 11 的具体版本。