https://github.com/Jairwin-L/ai-platform
AI 平台 monorepo:Next.js App Router 前台(platform)+ NestJS 接口服务(db-service)+ Vite React 管理后台(admin)。内置 React 19、TypeScript、Sass Module、Ant Design、alova 请求封装、Prisma/PostgreSQL、Redis、BYOK(自带 API Key)AI Chat、OpenAPI(Scalar)文档,以及 Docker + GitHub Actions 部署流程。
本仓库由 Vite+(vp)统一管理工作区:vpr(vp run 的独立简写)负责任务编排、依赖顺序与缓存,包管理由 vp install 驱动(底层是 pnpm workspace)。
apps/
platform/ Next.js 前台(@ai/platform),只负责页面;浏览器 /api/* 经 rewrites 转发到 db-service
db-service/ NestJS 接口服务(@ai/db-service),持有 Prisma schema / 迁移 / seed、Redis、全部业务接口
admin/ Vite + React 管理后台(@ai/admin),静态部署,直连 db-service
packages/
constants/ @ai/constants —— 与应用解耦的常量
utils/ @ai/utils —— 与应用解耦的纯工具函数
types/ @ai/types —— 与应用解耦的全局 ambient 类型声明
依赖方向为 platform / admin / db-service -> utils -> constants,types 独立无依赖。数据库只由 db-service 访问,platform 与 admin 都通过 HTTP 调用。
| 应用 | 开发端口 | 生产端口 |
|---|---|---|
| platform | 8060 | 8062 |
| admin | 8050(preview 8051) | 静态文件 |
| db-service | 8070 | 8072(容器内) |
lint / fmt / staged / verify 任务统一配置在根目录 vite.config.ts(规则拆在 vite.lint.config.ts、vite.fmt.config.ts),Stylelint 配置在根目录 stylelint.config.mjs,TS 公共选项在 tsconfig.base.json。
常用命令(在仓库根目录执行):
vp install # 安装依赖(同时安装 git hooks)
vpr dev # platform 开发服务,端口 8060
vpr dev:service # db-service 开发服务,端口 8070
vpr dev:admin # admin 开发服务,端口 8050
vpr dev:all # 三个应用并行启动
vpr build # platform 生产构建
vpr build:service # db-service 打包(先生成 Prisma Client)
vpr build:admin # admin 静态构建
vpr test # 全 workspace 单测(带缓存)
vpr check # 全 workspace 类型 + lint + 格式(带缓存)
vpr lint # check + Stylelint
vpr lint:fix # 自动修复
vpr prisma:generate # 生成 db-service 的 Prisma Client
vpr verify # 全量验证(Prisma Client + check + test + Stylelint),CI 使用单独操作某个项目(vpr <包名>#<脚本>,或用 -C 切到包目录):
vpr @ai/db-service#prisma:studio
vpr @ai/utils#test
vp -C apps/db-service run prisma:pushvpr 的常用选择器:-r 全 workspace、-F <包名|目录|glob> 过滤、-t 带上依赖、--parallel 并行、--cache / --no-cache 控制缓存、-v 查看执行摘要。
本仓库用 teamai-cli 统一管理 AI 协作资源(skills / rules / docs / hooks / MCP),采用单仓模式(mode: self):本仓库自己就是团队仓,知识随业务代码提交到 main,会话报告走 teamai-reports 孤儿分支。
.teamai/
teamai.yaml 团队配置(入库)
skills/ 团队 skills —— 唯一来源(入库)
rules/ 共享规则(入库)
docs/ 团队文档(入库)
agents/ subagent 定义(入库)
learnings/ 沉淀的会话知识(入库)
env/ 共享环境变量(入库,不放真实密钥)
hooks/ 团队 hooks(入库)
mcp/mcp.yaml 共享 MCP server(入库,不放 token)
config.yaml 本机配置(含绝对路径与用户名,不入库)
state.json 同步状态(不入库)
reports-wt/ teamai-reports 分支的 git worktree(不入库)
.teamai/.gitignore 声明上面哪些是机器本地状态;根 .gitignore 不要整目录忽略 .teamai/,否则 git 不会下降进去,那份声明会彻底失效。
各 AI 工具目录下的 skills/ 是 teamai pull 生成的实体副本,已在根 .gitignore 忽略:
.claude/skills/ ← 由 .teamai/skills/ 注入,勿手工编辑,勿入库
.codex/skills/ ← 同上
.claude/settings.json、.codex/hooks.json 团队 hooks,入库
.claude/settings.local.json 个人权限配置,不入库
新人 clone 后首次开 AI 会话时,SessionStart hook 会自动跑 teamai pull 把 skills 注入到工具目录;没装 teamai 的 hook 会静默跳过。手动初始化:
vp install -g teamai-cli
teamai init . --self --scope project --agent claude,codex常用命令:
teamai doctor # 诊断配置(认准输出里的 Scope 是 project)
teamai status # 本地与团队仓的差异、同步状态
teamai skill # 列出 REPO SKILLS 与各工具目录已注入的 skills
teamai pull # 拉团队资源并注入本地 AI 工具(会话启动时自动执行)
teamai push # 把本地新增资源提 PR 到本仓库新增一个团队 skill:直接放进 .teamai/skills/<name>/(带 SKILL.md),然后 teamai pull 注入本地验证,最后随业务代码一起提交。不要直接往 .claude/skills/ 里放 —— 那是产物目录,会被忽略且下次 pull 可能被覆盖。
本仓库是公开仓库,提交前请确认:
- 真实环境变量只放在未入库的
apps/*/.env(本地)或 GitHub Environments 的 secrets / vars(部署)。根.gitignore忽略所有.env/.env.*,只放行无敏感值的.env.example。 - 新增环境变量时同步更新对应应用的
.env.example(只写占位值)与部署 workflow:接口侧见apps/db-service/.env.example,前台见apps/platform/.env.example,后台见apps/admin/.env.example。 - admin 的
VITE_*、platform 的NEXT_PUBLIC_*会被编译进前端产物,只能放公开信息。 - 不提交密钥、token、私钥、数据库连接串、服务器 IP / SSH 信息、个人邮箱 / 手机号;
.teamai/env/、.teamai/mcp/同样只放无敏感值的配置。 - Compose 与部署脚本里的 PostgreSQL 默认口令只用于 Compose 内网(postgres 不暴露宿主端口),生产环境必须通过
POSTGRES_PASSWORD/DATABASE_URLsecret 覆盖。 - db-service 端口默认只绑定
127.0.0.1,由宿主机反向代理(HTTPS)对外提供给管理后台;生产环境/doc默认隐藏。 - 所有 PR 默认请求
@Jairwin-L审核(.github/CODEOWNERS);建议为main/dev开启 branch protection,并为productionenvironment 配置 Required reviewers。
- platform:Next.js App Router 前台页面(首页、登录注册、文章、AI Chat、个人中心),SSR 通过
API_INTERNAL_ORIGIN直连 db-service。 - db-service:NestJS 12 接口服务,统一响应封装与错误码、zod(Standard Schema)参数校验、Redis 会话(前台 / 后台会话域隔离)、限流、RBAC、BYOK 请求安全校验、R2 直传签名、图片压缩。
- admin:用户 / 角色 / 权限 / 系统设置 / AI Provider / 第三方服务管理;仅
SUPER_ADMIN/ADMIN可登录,登录密码 RSA-OAEP 加密传输。 - BYOK AI Chat:用户自带多 Provider API Key,服务端加密存储(见
apps/db-service/docs/byok-security.md)。 - Docker 多阶段构建:platform 镜像、db-service 镜像与 Prisma 迁移镜像;GitHub Actions 自动校验、构建 GHCR 镜像,并通过 SSH + Docker Compose 部署。
Next.js 16 · NestJS 12 · React 19 · TypeScript · Sass Module · Ant Design 6 · alova · Prisma 7 · PostgreSQL · Redis · Vite+ / pnpm · Docker / Docker Compose
- Node.js
22.18.0或更高的22.x版本 - Vite+ CLI
vp - pnpm,由 Vite+ 按项目配置使用
- PostgreSQL 与 Redis,本地开发可使用本机服务或 Docker
复制示例文件后填入真实值(.env / .env.local 均不入库):
cp apps/db-service/.env.example apps/db-service/.env
cp apps/platform/.env.example apps/platform/.env
cp apps/admin/.env.example apps/admin/.env.localdb-service 本地开发至少需要 DATABASE_URL、REDIS_URL、AUTH_CODE_SECRET;邮件验证码还需要 RESEND_API_KEY、RESEND_FROM_EMAIL;BYOK 需要 AI_KEY_ENCRYPTION_KEY_V1、AI_KEY_REDIS_ID_SECRET(openssl rand -base64 32 生成)。完整列表与说明见各应用的 .env.example。
platform 只需要 API_INTERNAL_ORIGIN(db-service 地址,本地 http://localhost:8070);它同时是 rewrites 的转发目标,在构建期写入。
同步数据库结构并写入基础角色 / 权限数据:
vpr @ai/db-service#prisma:setup启动开发服务:
vpr dev:all- platform:http://localhost:8060(
/、/sign-in、/sign-up、/articles、/ai/chat) - admin:http://localhost:8050(vite 代理
/api→ db-service) - db-service:http://localhost:8070/doc(Scalar API 文档,非生产环境默认开启)
首个管理员:管理端使用独立的系统账号(与 platform 注册账号分表)。在 apps/db-service/.env 里设置 BOOTSTRAP_ADMIN_ACCOUNT 与 BOOTSTRAP_ADMIN_PASSWORD(密码至少 6 位,只放本地 env,不要提交),执行 vpr @ai/db-service#prisma:seed 后再执行 vpr @ai/db-service#prisma:bootstrap-admin,即可用该账号登录管理端。
db-service 的其他脚本(均可用 vpr @ai/db-service#<脚本> 执行):
openapi:generate # 生成 openapi.json
prisma:generate # 生成 Prisma Client
prisma:migrate # 创建并执行本地迁移
prisma:push # 根据 schema 推送数据库结构,仅适合空库或临时开发
prisma:seed # 初始化种子:菜单 / 角色只在 RBAC 未初始化时写入,并同步 AI Provider、第三方服务选项
prisma:seed:menu # 以 prisma/data/menu/data.ts 为准重新同步菜单 / 按钮资源(会删除后台手动新增的菜单)
prisma:seed:role # 以 prisma/data/role/data.ts 为准重置种子角色(后台新建的角色保留)
prisma:bootstrap-admin # 按 BOOTSTRAP_ADMIN_ACCOUNT / PASSWORD 创建或补齐超级管理员系统账号
prisma:studio # 打开 Prisma Studio
prisma:deploy # 部署环境执行已提交的 Prisma migrations每个 app 自带一套 Docker 文件,仓库根目录没有 Dockerfile 和 docker-compose:
| 文件 | 说明 |
|---|---|
apps/platform/Dockerfile |
Next.js standalone,target runner |
apps/platform/docker-compose.yml |
部署基线,只有 app 一个服务,接入 db-service 栈的共享网络 |
apps/platform/docker-compose.build.yml |
本地 override:用源码构建代替拉 GHCR 镜像 |
apps/platform/scripts/deploy.sh |
platform 部署脚本 |
apps/db-service/Dockerfile |
NestJS(target service-runner)+ Prisma 工具(target prisma) |
apps/db-service/docker-compose.yml |
部署基线:postgres / redis / db-service / migrate,创建共享网络与数据卷 |
apps/db-service/docker-compose.dev.yml |
development 环境部署时自动叠加:把 postgres(5433)、redis(6380)暴露到宿主机 |
apps/db-service/docker-compose.build.yml |
本地 override:用源码构建代替拉 GHCR 镜像 |
apps/db-service/scripts/deploy.sh |
db-service 部署脚本,含 Prisma migrate deploy、种子数据、超级管理员 |
postgres 和 redis 只属于 db-service 栈。 platform 栈通过外部共享网络 SHARED_NETWORK(生产 ai-platform-prod-net,开发 ai-platform-dev-net)访问 http://db-service:<端口>,所以首次部署必须先部署 db-service。
默认端口:platform 8062(prod)/ 8060(dev),db-service 8072(prod)/ 8070(dev,默认只绑 127.0.0.1,管理后台经宿主机反向代理访问)。端口由各自的 scripts/deploy.sh 按环境固定注入,改端口要改脚本里的 default_app_port / default_service_port,并同步 platform 的 API_INTERNAL_ORIGIN。
本地用源码构建整套服务(命令在仓库根目录执行,Dockerfile 的构建 context 是仓库根目录):
export POSTGRES_PASSWORD=<本地密码>
docker compose -f apps/db-service/docker-compose.yml -f apps/db-service/docker-compose.dev.yml -f apps/db-service/docker-compose.build.yml --profile tools run --rm migrate
docker compose -f apps/db-service/docker-compose.yml -f apps/db-service/docker-compose.dev.yml -f apps/db-service/docker-compose.build.yml up -d --build
docker compose -f apps/platform/docker-compose.yml -f apps/platform/docker-compose.build.yml up -d --buildCompose 会自动加载
apps/db-service/.env做变量插值:其中给vpr dev用的DATABASE_URL/REDIS_URL指向localhost,在容器里连不上。本地跑 Docker 时先把这两个变量在 shell 里覆盖为postgresql://ai_platform:<本地密码>@postgres:5432/ai_platform?schema=public与redis://redis:6379/0。
手动构建镜像:
SHA="$(git rev-parse HEAD)"
docker build -f apps/platform/Dockerfile --target runner --build-arg API_INTERNAL_ORIGIN=http://db-service:8072 -t ghcr.io/<owner>/ai-platform:front-end-${SHA} .
docker build -f apps/db-service/Dockerfile --target service-runner -t ghcr.io/<owner>/ai-platform:service-${SHA} .
docker build -f apps/db-service/Dockerfile --target prisma -t ghcr.io/<owner>/ai-platform:prisma-${SHA} .platform 的 API_INTERNAL_ORIGIN 会被 next.config.ts 的 rewrites 在构建期写进 routes-manifest,必须通过 --build-arg 传入(缺失时构建直接失败);dev / prod 的 db-service 端口不同,两个环境各自构建 platform 镜像。
新增 packages/* 或 apps/* 时,需要同步 apps/platform/Dockerfile 与 apps/db-service/Dockerfile 中 deps 阶段的 COPY <dir>/package.json。
所有 workflow 只支持手动触发(Actions -> <workflow> -> Run workflow,选 dev 或 main 分支)。分支决定环境:main → production,dev → development。
| workflow | 构建镜像 | 部署内容 |
|---|---|---|
.github/workflows/db-service.yml |
<repo>:service-<sha>、<repo>:prisma-<sha> |
postgres / redis / db-service,执行 Prisma migrate deploy、种子数据、超级管理员 |
.github/workflows/platform.yml |
<repo>:front-end-<sha> |
只重启 platform 容器 |
.github/workflows/admin.yml |
无(静态资源) | scp 到服务器静态目录,整体替换 |
.github/workflows/deploy-all.yml |
复用上面三个 | 先 db-service,成功后按 targets 部署 platform / admin(都选时并行) |
.github/workflows/deploy-apps.yml |
复用 platform / admin | 并行发布 platform 与 admin,不动数据库 |
- db-service 只在
dev分支校验并打包;main分支不打包,直接复用服务器上 dev 栈(Compose projectai-platform-dev)正在运行的镜像,因此发布生产前先在dev跑一次 db-service。 - 镜像只有不可变的 commit SHA tag,没有
:latest;platform 与 db-service 共用同一个 GHCR 仓库,只靠 tag 前缀区分,部署脚本只清理本栈前缀的旧镜像,Prisma 工具镜像用完即删(KEEP_PRISMA_TOOL_IMAGE=true可保留)。 deploy-all的targets第一项是占位符,不选择部署范围时 workflow 在第一步失败,不会部署任何应用。- 菜单与角色种子只在库未初始化时自动执行;改了
apps/db-service/prisma/data/menu或role后,运行 db-service(或 deploy-all)并勾选run_menu_seed/run_role_seed。
服务器目录(两个栈各自一个目录,由变量直接指定,workflow 不会再往后拼子目录):
${DEPLOY_DB_SERVICE_PATH}/ # docker-compose.yml、docker-compose.dev.yml、scripts/deploy.sh、.env.<environment>
${DEPLOY_PLATFORM_PATH}/ # docker-compose.yml、scripts/deploy.sh、.env.<environment>
${DEPLOY_ADMIN_PATH}/ # admin 静态产物,每次部署整体清空重建,不要和其他文件混放
服务器 env 文件由 workflow 按 GitHub Environment 的 secrets / vars 每次整体生成覆盖。Compose project(db-service ai-platform-prod / ai-platform-dev,platform ai-platform-app-prod / ai-platform-app-dev)、共享网络、端口、库名(ai_platform / ai_platform_dev)等固定配置都写在两个 scripts/deploy.sh 里,不需要配到 GitHub Environment。数据卷名默认 <project>_postgres-data / <project>_redis-data,改 project 名或 POSTGRES_DATA_VOLUME 而不迁移旧卷,等同于换一个空库。
在 Settings -> Environments 中分别创建 production 与 development,各配置一份:
- 必填 Secrets:
DEPLOY_HOST、DEPLOY_USER、DEPLOY_SSH_KEY、POSTGRES_PASSWORD、AUTH_CODE_SECRET、AI_KEY_ENCRYPTION_KEY_V1、AI_KEY_REDIS_ID_SECRET、RESEND_API_KEY、R2_ACCESS_KEY_ID、R2_SECRET_ACCESS_KEY - 必填 Variables(或 Secrets):
DEPLOY_DB_SERVICE_PATH、DEPLOY_PLATFORM_PATH、DEPLOY_ADMIN_PATH、RESEND_FROM_EMAIL、R2_ENDPOINT_URL、R2_BUCKET_NAME、VITE_BASE_API_URL(db-service 的公网 HTTPS 地址,admin 构建期写入) - 可选 Secrets:
DEPLOY_PORT(默认22)、GHCR_READ_TOKEN(默认回落GITHUB_TOKEN)、DATABASE_URL(默认由POSTGRES_*推导)、REDIS_URL、AI_SECRET_MASTER_KEY、BOOTSTRAP_ADMIN_PASSWORD - 可选 Variables:
BOOTSTRAP_ADMIN_ACCOUNT(与BOOTSTRAP_ADMIN_PASSWORD同时配置时,部署会确保该超级管理员系统账号存在;已存在的账号不会被重置密码)、CORS_ALLOWED_ORIGINS(必须包含管理后台域名)、RESEND_FROM_NAME、ENABLE_API_DOCS、SERVICE_BIND(默认127.0.0.1)、BYOK_TRUST_PROXY_HEADERS(默认true)、API_INTERNAL_ORIGIN(默认http://db-service:8072/http://db-service:8070)、VITE_APP_TITLE、VITE_PLATFORM_URL
建议在 Settings -> Environments -> production 开启 Required reviewers,避免 main 部署绕过人工确认。
也可以在服务器上手动执行部署脚本(两个脚本都带 --help):
SHA=<要部署的 commit sha>
# 先部署 db-service:它创建共享网络、postgres、redis,并执行 Prisma migrate deploy 与种子
cd ${DEPLOY_DB_SERVICE_PATH}
SERVICE_IMAGE=ghcr.io/<owner>/ai-platform:service-${SHA} scripts/deploy.sh production
# 再部署 platform
cd ${DEPLOY_PLATFORM_PATH}
APP_IMAGE=ghcr.io/<owner>/ai-platform:front-end-${SHA} scripts/deploy.sh productiondevelopment 环境的 postgres / redis 默认发布到宿主机 0.0.0.0:5433 / 0.0.0.0:6380,方便本机直连 dev 库;必须在服务器防火墙和云安全组中只放行可信 IP。生产环境不暴露这两个端口。
服务器地址、SSH 私钥等只存在于 GitHub Environments,仓库中不出现任何真实值。
详见 AGENTS.md(CLAUDE.md 为其软链接)。要点:
- platform 请求优先复用
apps/platform/src/api/alova.ts与apps/platform/src/api/业务请求模块;SSR 取数用apps/platform/src/api/server.ts。 - 新接口写在
apps/db-service/src/modules/,参数用 zod schema 通过@Body({ schema })/@Query({ schema })校验,鉴权用Auth/OptionalAuth(前台)与AdminPermissionAuth/AdminAnyPermissionAuth(管理端,权限码见@ai/constants/permissions)装饰器。 - Server Component / Client Component 按需区分,只有存在客户端交互时才添加
"use client"。 utils相关文件需要保留 JSDoc@file、@func、@desc、@param和@returns说明,工具函数使用function声明。- 多个异步任务并发时使用
Promise.allSettled并显式处理成功和失败结果。 - 提交信息使用 Conventional Commits(英文),例如
feat: add article management。