@wecode-team/cms-supabase-api
v0.1.31
Published
A CMS API package using Hono framework with Supabase and dynamic table management
Readme
@wecode-team/cms-supabase-api
一个基于 Hono 框架的动态 CMS API 包,使用 Supabase 作为后端数据库,支持动态表管理、数据关联、用户认证等功能。
📖 设计理念
核心思想
本包的核心理念是 "JSON Schema 驱动的动态 CMS":
- 模型即配置:通过 JSON Schema 定义数据模型,系统自动创建对应的数据库表
- 动态表管理:无需手写 SQL,通过 API 动态创建、修改、删除数据表
- 关系支持:支持
belongsTo、hasMany、belongsToMany三种关联关系 - 多租户隔离:通过表名前缀(Session ID)实现数据隔离
架构设计
┌─────────────────────────────────────────────────────────────┐
│ Hono Application │
├─────────────────────────────────────────────────────────────┤
│ Route Helpers │
│ ├── createCmsRoutes() 一键创建所有路由 │
│ ├── createModelRoute() 模型管理路由 │
│ ├── createDynamicDataRoute() 动态数据路由 │
│ └── createDynamicAuthRoute() 动态认证路由 │
├─────────────────────────────────────────────────────────────┤
│ Handlers (处理器层) │
│ ├── models.ts 模型 CRUD │
│ ├── data.ts 数据 CRUD + 关联查询 │
│ └── auth.ts 登录/认证/用户管理 │
├─────────────────────────────────────────────────────────────┤
│ Services (服务层) │
│ ├── DynamicTableService 动态表操作 │
│ ├── CmsModelService 模型配置管理 │
│ └── AuthService 认证服务 │
├─────────────────────────────────────────────────────────────┤
│ Supabase (数据层) │
│ ├── PostgreSQL 关系型数据库 │
│ ├── RPC Functions 存储过程 │
│ └── Row Level Security 行级安全 │
└─────────────────────────────────────────────────────────────┘🚀 安装
npm install @wecode-team/cms-supabase-api
# 或
pnpm add @wecode-team/cms-supabase-api
# 或
yarn add @wecode-team/cms-supabase-api📋 依赖
Peer Dependencies(必须安装)
{
"hono": "^4.0.0",
"@supabase/supabase-js": "^2.0.0"
}🔧 快速开始
第一步:初始化 Supabase
在 Supabase SQL 编辑器中执行 supabase-setup.sql 文件中的 SQL 脚本:
-- 执行完整的 supabase-setup.sql 文件
-- 这将创建必要的函数和表🔐 Auth(登录/注册/权限)方案
本项目的 CMS 鉴权支持:
- 首次进入需要注册管理员
- 每个
session_id仅一个管理员 session_id维度权限隔离- Supabase Auth 邮箱重写(避免跨 session 冲突)
- 不依赖
execute_sql(需要的 registry 表由你手动创建)
详见:AUTH_SCHEME.md
第二步:创建 Hono 应用
import { Hono } from 'hono'
import { cors } from 'hono/cors'
import {
initializeSupabase,
initializeCmsSystem,
createCmsRoutes
} from '@wecode-team/cms-supabase-api'
const app = new Hono()
// CORS 配置
app.use('/*', cors())
// 初始化 Supabase
initializeSupabase({
url: process.env.SUPABASE_URL!,
key: process.env.SUPABASE_ANON_KEY!
})
// 初始化 CMS 系统表
await initializeCmsSystem()
// 一键创建所有 CMS 路由
createCmsRoutes(app)
export default app第三步:定义数据模型
通过 API 创建模型:
// POST /models
{
"name": "文章模型",
"table_name": "posts",
"json_schema": {
"fields": [
{
"name": "title",
"type": "string",
"comment": "文章标题",
"required": true,
"maxLength": 255
},
{
"name": "content",
"type": "text",
"comment": "文章内容",
"required": true
},
{
"name": "author",
"type": "relation",
"comment": "作者",
"required": true,
"relation": {
"type": "belongsTo",
"target": "users",
"foreignKey": "author_id",
"displayField": "name"
}
},
{
"name": "status",
"type": "string",
"comment": "状态",
"defaultValue": "draft"
}
]
}
}📚 API 路由
模型管理
| 方法 | 路径 | 描述 |
|------|------|------|
| GET | /models | 获取所有模型 |
| POST | /models | 创建新模型(同时创建数据表) |
| PUT | /models | 更新模型 |
| DELETE | /models?id={id} | 删除模型(同时删除数据表) |
数据管理
| 方法 | 路径 | 描述 |
|------|------|------|
| GET | /data/:tableName | 获取表数据(支持分页、搜索) |
| GET | /data/:tableName/with-relations | 获取带关联数据的表数据 |
| POST | /data/:tableName | 创建新数据 |
| PUT | /data/:tableName | 更新数据 |
| DELETE | /data/:tableName?id={id} | 删除数据 |
关联数据
| 方法 | 路径 | 描述 |
|------|------|------|
| GET | /relation/:tableName/options | 获取关联表选项(用于下拉选择) |
认证
| 方法 | 路径 | 描述 |
|------|------|------|
| GET | /auth/signup/status/:tableName | 查询当前 session 是否允许首次注册管理员 |
| POST | /auth/signup/:tableName | 首次注册管理员(服务端代理 Supabase signUp) |
| POST | /auth/:tableName/login | 用户登录 |
| GET | /auth/:tableName/current | 获取当前用户信息 |
| POST | /auth/:tableName/verify | 验证 Token |
| GET | /auth/user/:tableName | 获取当前用户信息(兼容历史路径) |
| POST | /auth/verify/:tableName | 验证 Token(兼容历史路径) |
提示:后台管理接口(
/models、/data/*等)建议携带请求头X-Session-Id,用于按session_id维度校验管理员权限。
🏗️ 字段类型
| 类型 | PostgreSQL 类型 | 说明 |
|------|-----------------|------|
| string | text | 短文本 |
| text | text | 长文本 |
| integer | int4 | 整数 |
| float | float8 | 浮点数 |
| boolean | bool | 布尔值 |
| date | date | 日期 |
| datetime | timestamptz | 日期时间 |
| json | jsonb | JSON 对象 |
| email | text | 邮箱(前端验证) |
| relation | uuid/int4 | 关联字段(外键) |
🔗 关系配置
belongsTo(多对一)
{
"name": "author",
"type": "relation",
"relation": {
"type": "belongsTo",
"target": "users", // 关联的目标表
"foreignKey": "author_id", // 当前表的外键字段
"displayField": "name" // 下拉显示的字段
}
}hasMany(一对多)
{
"name": "posts",
"type": "relation",
"relation": {
"type": "hasMany",
"target": "posts",
"foreignKey": "author_id" // 目标表的外键字段
}
}🔐 多租户支持(Session ID)
通过表名前缀实现数据隔离:
// 前端设置 Session ID
setSessionId("tenant123")
// API 调用时自动添加前缀
// GET /data/posts → 实际查询 tenant123_posts 表🛠️ 高级用法
自定义路由
import { Hono } from 'hono'
import {
createModelRoute,
createDynamicDataRoute,
createAuthRoute
} from '@wecode-team/cms-supabase-api'
const app = new Hono()
// 只创建模型路由
createModelRoute(app)
// 只创建数据路由
createDynamicDataRoute(app)
// 创建固定表名的认证路由
createAuthRoute(app, 'cms_users')使用认证中间件
import { requireAuth, getModels } from '@wecode-team/cms-supabase-api'
// 保护路由
app.get('/protected/models', requireAuth(getModels))直接使用服务层
import {
getDynamicTableService,
getCmsModelService
} from '@wecode-team/cms-supabase-api'
const tableService = getDynamicTableService()
const modelService = getCmsModelService()
// 创建表
await tableService.createTable('my_table', {
fields: [
{ name: 'title', type: 'string', required: true }
]
})
// 插入数据
await tableService.insertData('my_table', { title: 'Hello' })🔧 环境变量
| 变量 | 说明 | 默认值 |
|------|------|--------|
| SUPABASE_URL | Supabase 项目 URL | - |
| SUPABASE_ANON_KEY | Supabase 匿名密钥 | - |
| JWT_SECRET | JWT 签名密钥 | 'your-secret-key-change-in-production' |
🐛 常见问题
1. 外键约束错误
如果遇到 violates foreign key constraint 错误,是因为数据库中的外键约束引用了错误的表。解决方法:
-- 删除错误的外键约束
ALTER TABLE "your_table" DROP CONSTRAINT IF EXISTS "constraint_name";2. CHECK 约束错误
如果遇到 violates check constraint 错误:
-- 删除 CHECK 约束
ALTER TABLE "your_table" DROP CONSTRAINT IF EXISTS "table_status_check";3. 函数不存在错误
如果遇到 Could not find the function 错误,需要执行初始化 SQL:
-- 执行 supabase-setup.sql 中的所有语句📄 许可证
MIT
