Scaffold
Skill TNT-Likely/honeycomb/plugins/fastapi-vite-saas/skills/scaffold
🍯 AI 开发能力沉淀仓 — 跨工具可复用的 skills 集合(Claude Code plugin marketplace 分发,SKILL.md 跨工具可用)
npx -y skills add TNT-Likely/honeycomb --skill scaffoldAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
当用户要起一个 FastAPI 后端 + Vite SPA 前端 + Docker + GitHub Actions 的单仓双端 SaaS 项目时使用。一个 docker run 就跑起来的自托管 SaaS / 内部工具骨架,脱胎于 BeeCount-Cloud 生产实践。触发关键词:"scaffold FastAPI + Vite 项目"、"新建一个 SaaS 单仓"、"起一个 FastAPI Docker monorepo"、"按 fastapi-vite-saas / BeeCount-Cloud 风格建项目"、"create a fastapi vite saas template"、"new selfhost monorepo with FastAPI and Vite"。不适用于纯前端项目、纯 CLI 工具、纯库项目。
SKILL.md
13.6 KB, as published. Nobody here has run it
FastAPI + Vite SaaS 脚手架技能
这套架构是什么
一个单仓双端的全栈应用骨架:Python 后端 + 前端 SPA 共住一个 git 仓库,通过多阶段 Docker 构建打成单一镜像,GitHub Actions 自动发版。核心定位:用一个 docker run 就能跑起来的自托管 SaaS。
技术选型(每一项都解释为什么):
| 层 | 选型 | 为什么 |
|---|---|---|
| Web framework | FastAPI | Pydantic v2 校验直接当 schema、async 原生、OpenAPI 自动生成 |
| ORM | SQLAlchemy 2.x | 久经考验、async 支持成熟、Alembic 配套迁移成熟 |
| Migrations | Alembic | 不要自己搞 schema_migrations 表 |
| Auth | JWT(PyJWT)+ passlib + 可选 pyotp 2FA | 无状态、单镜像无 redis 依赖 |
| 调度 | APScheduler BackgroundScheduler | in-process,不引入额外服务;线程池避免阻塞事件循环 |
| 备份加密 | pyzipper(AES-256 zip) | stdlib zipfile 的 ZipCrypto 已破,pyzipper 是 drop-in 替换 |
| 对象存储 | rclone subprocess | 一个二进制覆盖 S3/R2/WebDAV/B2/GDrive/OneDrive |
| Lint/Type/Test | ruff + mypy + pytest | 标配,启动快 |
| Frontend | pnpm workspace + Vite + (React/Vue 任选) | apps/* + packages/* 拆分清晰 |
| 构建产物 | 单一 Docker 镜像 | 多阶段:Node build 前端 → Python 运行时拷 dist/ 当静态 |
| CI | GitHub Actions | ci.yml(test/lint/build)+ release.yml(tag 触发 → docker hub) |
| 编排 | Makefile | 不引入 just/task,Makefile 一份所有人都会读 |
仓库布局(基线)
<project>/
├── .github/
│ └── workflows/
│ ├── ci.yml # PR/push: ruff + mypy + pytest + frontend build
│ └── release.yml # tag 'v*': build & push docker image
├── .docs/ # 内部设计文档(不发布)
├── docs/ # 用户向文档(随发版打入镜像/网站)
├── alembic/
│ ├── env.py
│ ├── script.py.mako
│ └── versions/
├── data/ # 运行时数据(gitignored,.gitkeep 占位)
│ └── .gitkeep
├── frontend/
│ ├── pnpm-workspace.yaml
│ ├── package.json # 仅 workspace 根
│ ├── apps/
│ │ └── web/ # Vite SPA
│ └── packages/
│ ├── api-client/ # 类型化 API client(从 OpenAPI 生成)
│ ├── ui/ # 共享组件
│ └── features/ # 跨页可复用业务组件
├── scripts/ # 运维脚本(seed/backup/grant-admin/...)
├── src/ # Python package(直接 import,不再嵌一层)
│ ├── __init__.py
│ ├── main.py # FastAPI app + lifespan
│ ├── config.py # pydantic-settings,所有 env 集中定义
│ ├── database.py # engine / session / Base
│ ├── deps.py # FastAPI Depends 工厂(get_db / get_user / ...)
│ ├── models.py # SQLAlchemy ORM(小项目放一起,大了再拆)
│ ├── schemas.py # Pydantic schemas(同上)
│ ├── security.py # 密码哈希 + JWT 签发/校验 + 可选 TOTP
│ ├── error_handling.py # 全局异常 → JSON 响应映射
│ ├── observability.py # structured logging + metrics
│ └── routers/
│ └── <group>/
│ ├── __init__.py # 聚合 APIRouter,main.py import 不变
│ ├── _shared.py # 共享 imports/helpers/router 实例;__all__ 显式
│ └── <entity>.py # 单一资源的 POST/PATCH/DELETE 或一组 GET
├── tests/ # pytest,与 src/ 对称
├── .env.example # 所有 env 的注释样本(.env 进 gitignore)
├── .gitignore
├── alembic.ini
├── docker-compose.yml # 生产 compose(用户拿去 deploy)
├── docker-compose.postgres.yml # overlay,可选切换 Postgres
├── Dockerfile # 多阶段:frontend-builder → python:3.12-slim
├── Makefile # 唯一的命令编排入口
├── mypy.ini
├── pytest.ini
├── README.md / README.en.md # 双语
├── requirements.txt # 不用 pyproject — 简单项目 requirements 更直白
├── ruff.toml
└── server.py # 极简 entry:from src.main import app
几个不显眼但关键的设计:
server.py在仓根:让uvicorn server:app能直接跑。生产 Dockerfile 也用这个,本地 dev 也用这个,统一入口避免本地/生产两套启动方式。src/不再嵌套包名:小项目里<project>/src/<project>/foo.py是没必要的间接层。直接src/foo.py,Python path 设.(PYTHONPATH=.)即可。- 路由用包不用文件:每个
routers/<group>/是包,_shared.py集中共享导入和 router 实例。新增 endpoint 只动单个 entity 文件,改共享逻辑只动_shared.py。不要把业务塞进__init__.py,它只做聚合。 /data是唯一持久化目录:DB、备份、附件、用户上传 — 全部走DATA_DIRenv,默认/data(docker)或./data(本地)。容器部署只挂一个 volume 就能整体备份。.docs/vsdocs/:点前缀的不发布(内部设计/复盘),无点前缀的对外。明确分流。
用户工作流(Makefile 是核心)
scaffold 完成后,用户应该能直接:
make setup-backend # 建 venv + pip install + 拷 .env.example → .env
make migrate # alembic upgrade head
make seed-demo # 灌种子数据(可选)
make dev-api # uvicorn --reload --host 0.0.0.0
make dev-web # pnpm install + vite dev
make dev-db # docker compose up postgres(如果切到 PG)
make test # pytest
make lint # ruff check
make typecheck # mypy src
make wipe-local # 清本地 dev 数据(保留 .gitkeep 和指定的构建产物)
dev-api 必须 --host 0.0.0.0:模拟器/真机经 WiFi 用 IP 访问时,uvicorn 默认绑 127.0.0.1 会 Connection refused。这条踩过,scaffold 时直接写好。
scaffold 流程(AI 执行步骤)
当用户说"按 fastapi-vite-saas 起一个新项目叫 Foo"时,按以下顺序执行:
Step 1: 收集参数
如果用户没给全,问清楚(一次性问,不要来回往返):
- 项目名(kebab-case,如
foo-cloud)→ 同时确定 Python 包名(snake_case)、Docker image 名、env 前缀 - 目标路径(默认
~/code/mine/<project-name>) - 前端框架:React / Vue / Svelte(默认 React,跟 BeeCount-Cloud 一致)
- 默认数据库:SQLite(单镜像零依赖)/ Postgres(团队/生产强一致需求)— 默认 SQLite,Postgres 通过 overlay compose 切换
- 是否需要 2FA(默认否,简化首版)
- 是否需要备份模块(rclone + 加密 zip,默认否,有需要再加)
- GitHub owner(用户名/组织名,写进 Dockerfile/README/CI)
Step 2: 创建目录树 + 生成基础文件
按上面的"仓库布局"创建空目录(包括 .gitkeep 占位)。
然后生成以下文件,所有 {{project_name}} / {{python_pkg}} / {{github_owner}} / {{image_name}} 替换成 Step 1 参数:
requirements.txt— 基线依赖(从这个 skill 同目录的templates/requirements.txt拷,见下面"模板文件")src/main.py— FastAPI app 骨架,挂 CORS、健康检查/healthz、版本端点/api/v1/versionsrc/config.py— pydantic-settings,声明所有 env(DATABASE_URL, JWT_SECRET, DATA_DIR, CORS_ORIGINS, APP_ENV, APP_VERSION)src/database.py— engine(根据 DATABASE_URL 自动判 sqlite/pg)+ SessionLocal + Basesrc/deps.py— get_db / get_current_user 占位src/security.py— passlib CryptContext + JWT encode/decodesrc/models.py— User 模型占位src/schemas.py— UserCreate/UserRead/Token 占位src/error_handling.py— 把常见异常映射成 JSON(401/403/404/422/500)src/routers/auth/__init__.py+_shared.py+register.py+login.py— 完整可跑的注册登录端点alembic.ini+alembic/env.py+ 第一个 migration(创建 users 表)server.py— 一行:from src.main import appMakefile— 全套 dev/test/lint/typecheck/wipe-localDockerfile— 多阶段构建(参考 BeeCount-Cloud,删掉 docs-index 那一段,普通项目不需要 RAG 索引)docker-compose.yml— 单服务,挂./data:/data,暴露端口可参数化(默认 8080 → host)docker-compose.postgres.yml— overlay,定义dbservice + 覆盖DATABASE_URL.env.example— 所有 env 的注释样本,JWT_SECRET 写change-me-32-bytes-strong-random.gitignore— Python + Node + venv + .env + data/* (保留 .gitkeep) + IDEruff.toml/mypy.ini/pytest.ini— 直接抄 BeeCount-Cloud 的配置.github/workflows/ci.yml— matrix: backend(ruff + mypy + pytest)+ frontend(pnpm install + build).github/workflows/release.yml— tag 触发,build docker → push docker hub(用{{github_owner}}/{{image_name}})README.md+README.en.md— 模板文档(项目简介、quickstart、deploy、env 说明)frontend/pnpm-workspace.yaml+frontend/package.jsonfrontend/apps/web/— Vite + React/Vue 模板(用pnpm create vite思路,生成最小可跑)frontend/packages/api-client/— 占位 + 注释说明"从 OpenAPI 用 openapi-typescript 生成"frontend/packages/ui/+frontend/packages/features/— 占位 + 一个 hello 组件
Step 3: 初始化 git + 装依赖 + 跑一次自检
cd <target>
git init && git add -A && git commit -m "feat: fastapi-vite-saas 脚手架初始化"
make setup-backend
make migrate
make test # 期望:0 个测试,exit 0
cd frontend && pnpm install
pnpm -C apps/web build # 期望:dist/ 产出
任何一步失败,立即停下报错给用户,不要试图绕过。
Step 4: 提示下一步
最后给用户一份"接下来要干的 5 件事"清单:
- 改
.env.example的JWT_SECRET为真随机串(给一行openssl rand -hex 32命令) - 把仓库推到 GitHub:
gh repo create {{github_owner}}/{{project_name}} --public --source . --push - 配 Docker Hub secrets(
DOCKERHUB_USERNAME/DOCKERHUB_TOKEN)让 release.yml 能 push 镜像 - 改 README 里的 placeholder(项目简介、screenshot)
- 起第一个业务实体:跟 AI 说"加一个 <实体名> 资源",AI 应该按 routers/<group>/ + models.py + schemas.py + alembic migration 这套流程加
不要从 BeeCount-Cloud 抄过来的部分
以下是 BeeCount-Cloud 的领域逻辑,新项目里不要默认带上:
sync_changes+read_*_projection+sync_applier.py那套多端同步架构(只在需要"移动端离线 + 多设备同步"时才搭)- AI 文档 Q&A / RAG /
numpycosine search /docs-index.*.sqlite(只在产品本身要做文档搜索时才搭) mcp/目录 MCP server(只在要给 LLM 工具调用时才搭)pyzipper/rclone/apscheduler备份链(在用户明确说要做"自助备份到对象存储"时才搭)pyotp/ TOTP 2FA(只在 step 1 用户明确说要 2FA 时才搭)openpyxlExcel 导入(域特定,不要默认装)
每一项都增加运行时复杂度。默认 scaffold 是最小可跑的 FastAPI + Vite + Docker,其它当 add-on 按需启用。
模板文件位置
与 SKILL.md 同目录,有一个 templates/ 子目录,里面是上面 Step 2 列出的所有文件的实际模板内容(用 {{var}} 占位)。AI 执行时:
- 读
templates/<filename>内容 - 用 Step 1 收集的参数做字符串替换
- 写到
<target>/<filename>
模板暂未填充时,AI 可以基于 BeeCount-Cloud 对应文件现场改写(把 beecount / BeeCount / 8869 / sunxiao0721 等字面量替换成新项目参数)。后续版本会把模板固化进 templates/,减少现场改写的随机性。
反模式(已踩过的坑)
- ❌ 不要在
server.py里写业务,它只能是from src.main import app一行 — 否则本地/生产入口分裂 - ❌ 不要用硬编码 SQL
CREATE TABLE ... AS SELECT做迁移,全走 Alembic - ❌ 不要在 Drift/Alembic 之外维护自己的
schema_migrations表 - ❌ Dockerfile 里不要在 frontend builder 阶段就
COPY frontend/全量 — 先只拷package.json+ lock,装完依赖再 COPY 全量,Docker layer cache 才能命中 - ❌ 不要把
data/留在仓里跟构建产物混(docs-index.*.sqlite是个例外:它是从兄弟仓 build 出来 commit 进去的索引) - ❌ HEALTHCHECK 不要打
/api/v1/healthz,挂在根/healthz,避免被前缀路由拦掉
升级与变体
- 切 Postgres:
docker-compose -f docker-compose.yml -f docker-compose.postgres.yml up,.env改DATABASE_URL=postgresql+psycopg://... - 切 Vue/Svelte:Step 1 选项分支,生成
frontend/apps/web/时换模板 - 加 MCP server:用
mcp>=1.27.0装 FastMCP,挂在/api/v1/mcp,PAT 鉴权 - 加备份模块:装 pyzipper + apscheduler,起一个
src/backup/包,scripts/backup_sqlite.sh兜底 CLI