根据《生成式人工智能服务管理暂行办法》的要求,请勿对中国地区公众提供一切未经备案的生成式人工智能服务。
基于 Next.js 的多租户 LLM 网关 + 管理控制台,支持 SQLite 和 MySQL 双存储驱动。
- Next.js (App Router + Route Handlers)
- SQLite (
better-sqlite3) / MySQL (mysql2) - JWT 认证 (access + refresh token)
- 内存令牌桶限流
- 多渠道管理 — 支持配置多个上游提供商,加权负载均衡 + 熔断器
- 模型别名路由 — 自动协议转换,客户端无感知
- 多协议支持:
POST /api/v1/chat/completions— OpenAI Chat CompletionsPOST /api/ollama/api/chat— Ollama ChatGET /api/ollama/api/tags— Ollama 模型列表POST /api/ollama/api/show— Ollama 模型详情GET /api/ollama/api/version— Ollama 版本探测POST /api/v1/responses— OpenAI ResponsesPOST /api/v1/messages— Anthropic Claude MessagesPOST /api/v1/embeddings— OpenAI EmbeddingsGET /api/v1/models— 获取可用模型列表
- 用户与角色 — 管理员 / 普通用户,API Key 自助管理
- 用户组 — 组级限流 (QPS/RPM/TPM)、配额、模型白名单,支持继承机制 (
用户 > 组 > 全局默认) - OIDC 单点登录 — 通用 OIDC 提供商接入,支持自动注册、通过 claim 映射用户组
- 免认证模式 — 适用于局域网单用户部署,无需登录
- 请求日志 — 记录模型、渠道、状态码、Token 用量、延迟等信息
- 用户自助 — 管理密钥、修改密码、绑定/解绑 OIDC
- 管理后台 — 渠道、模型、用户、用户组、系统设置、日志
pnpm install
pnpm dev打开 http://localhost:3000,首个注册用户自动成为管理员。
项目提供预构建镜像,可通过 Docker Compose 快速部署:
docker compose up -d| 镜像 | 说明 |
|---|---|
docker.cnb.cool/ecustcic/modelgate:latest |
完整版 |
docker.cnb.cool/ecustcic/modelgate:lite |
精简版 |
按需修改 docker-compose.yml 中的 image 字段即可切换版本。
通过环境变量或 .env 文件配置:
| 变量 | 说明 |
|---|---|
JWT_ACCESS_SECRET |
Access Token 签名密钥,生产环境务必替换为随机字符串 |
JWT_REFRESH_SECRET |
Refresh Token 签名密钥,同上 |
AUTH_DISABLED=1 |
取消注释可开启免认证模式(单用户/局域网场景) |
DB_DRIVER=mysql |
切换为 MySQL 存储,需同时配置下方 MySQL 相关变量 |
MYSQL_HOST / MYSQL_PORT |
MySQL 连接地址(默认 localhost:3306) |
MYSQL_USER / MYSQL_PASSWORD |
MySQL 用户名和密码 |
MYSQL_DATABASE |
MySQL 数据库名(默认 modelgate) |
- SQLite(默认):通过 Docker volume
modelgate-data持久化,容器重建后数据不会丢失 - MySQL:数据存储在外部 MySQL 实例中,无需 volume 挂载
默认构建为完整版,包含 OIDC、周期配额、系统公告等完整功能:
npm run dev:full
npm run build精简版在构建时关闭 OIDC、周期配额、系统公告、Webhook 回调,相关页面不渲染,相关 API 功能不可用或忽略对应字段:
npm run dev:lite
npm run build:liteDocker 构建可通过 build arg 选择版本:
docker build --build-arg MODELGATE_EDITION=lite -t modelgate:lite .CNB 流水线中,main push 只负责自动生成 tag;tag_push 会构建完整版镜像并推送 latest 和 tag 名,同时构建精简版镜像并推送 lite。
全部可选,均有默认值:
| 变量 | 默认值 | 说明 |
|---|---|---|
JWT_ACCESS_SECRET |
dev-access-secret-change-me |
Access Token 签名密钥 |
JWT_REFRESH_SECRET |
dev-refresh-secret-change-me |
Refresh Token 签名密钥 |
JWT_ACCESS_EXPIRES_SECONDS |
900 |
Access Token 有效期(15 分钟) |
JWT_REFRESH_EXPIRES_SECONDS |
604800 |
Refresh Token 有效期(7 天) |
AUTH_DISABLED |
— | 设为 1 关闭所有认证(单用户模式) |
DB_DRIVER |
sqlite |
设为 mysql 启用 MySQL 存储驱动 |
MYSQL_HOST |
localhost |
MySQL 主机地址 |
MYSQL_PORT |
3306 |
MySQL 端口 |
MYSQL_USER |
root |
MySQL 用户名 |
MYSQL_PASSWORD |
— | MySQL 密码 |
MYSQL_DATABASE |
modelgate |
MySQL 数据库名 |
MYSQL_POOL_SIZE |
10 |
MySQL 连接池大小 |
MODELGATE_EDITION / NEXT_PUBLIC_MODELGATE_EDITION |
full |
构建版本,full 为完整版,lite 为精简版 |
适用于局域网内单人使用,无需注册登录:
AUTH_DISABLED=1 pnpm dev- 网关端点跳过 API Key 验证
- 控制台跳过 JWT 认证,直接以管理员身份访问
- 登录 / 注册页面自动跳转到控制台
- 自动创建内置
noauth管理员用户和 API Key - 限流、日志、配额照常生效(挂在该内置用户下)
- 不设置此变量则完全不影响现有部署
默认使用 SQLite(data/gateway.db,首次运行自动创建)。切换 MySQL 需设置 DB_DRIVER=mysql 并配置连接参数:
DB_DRIVER=mysql
MYSQL_HOST=127.0.0.1
MYSQL_PORT=3306
MYSQL_USER=modelgate
MYSQL_PASSWORD=your-password
MYSQL_DATABASE=modelgateMySQL 模式下表结构会在首次连接时自动创建,无需手动导入建表脚本。
数据表:channels、models、users、groups、keys、settings、logs
POST /api/auth/register— 受允许账号密码注册设置控制POST /api/auth/login— 受允许账号密码登录设置控制POST /api/auth/refresh— 刷新令牌GET /api/auth/me— 获取当前用户信息POST /api/auth/change-password— 修改密码POST /api/auth/logout— 退出登录
用户名仅允许英文字母和数字([A-Za-z0-9]),最少 3 位。
在管理后台「系统设置」页面配置:
- Issuer URL — OIDC 提供商地址(需支持
.well-known/openid-configuration) - Client ID / Secret — 从 OIDC 提供商获取
- 回调地址 — 设置页面中展示,复制到 OIDC 提供商配置即可
- 自动注册 — 首次 OIDC 登录时自动创建用户
- 用户组映射 — 在各用户组中配置 Claim 表达式,按 OIDC claims 自动分配用户组
接口:
| 端点 | 说明 |
|---|---|
GET /api/auth/oidc/status |
公开接口,返回各登录方式的可用状态 |
GET /api/auth/oidc/authorize |
发起 OIDC 授权流程 |
GET /api/auth/oidc/callback |
处理 OIDC 提供商回调 |
GET /api/auth/oidc/bind |
已登录用户绑定 OIDC 账号 |
POST /api/auth/oidc/unbind |
已登录用户解绑 OIDC 账号 |
管理员可独立开关账号密码登录和 OIDC 登录,但至少需保留一种登录方式。
用户组定义用户继承的基线配置:
- 限流:QPS、RPM、TPM
- 配额:请求总量配额、Token 总量配额
- 模型白名单:组级白名单与用户级白名单取并集
继承优先级:用户设置 > 组设置 > 全局默认。用户的限流值为 -1 表示继承组设置。
OIDC 组映射:在各用户组中配置 Claim 表达式(如 role == "vip" 或 tags contains "premium")。用户通过 OIDC 登录时自动分配到匹配的组,每次登录自动同步。
| 接口范围 | 权限要求 |
|---|---|
/api/admin/* |
管理员角色 |
/api/dashboard/* |
已认证 Web 用户,按接口返回本人或管理员可见数据 |
/api/user/* |
已认证用户,仅限本人资源 |
/api/v1/*、/api/ollama/* |
API Key 认证 |
所有接口均支持两种认证方式:
- Session 认证:JWT Cookie(Web 控制台自动管理)
- API Key 认证:
Authorization: Bearer sk-gw-...、x-api-key: sk-gw-...,或 query 参数?token=sk-gw-.../?api_key=sk-gw-...
Query 鉴权主要用于无法自定义请求头的客户端;能设置请求头时仍推荐使用 Authorization 或 x-api-key。
Ollama 兼容接口仅放在 /api/ollama/* 下。可以通过请求头、query 参数或 path 传递 API Key;Ollama 客户端如果会自行追加 /api/version、/api/tags、/api/show、/api/chat 或 /v1/chat/completions,可将服务根地址配置为 http://localhost:3000/api/ollama/sk-gw-xxxx,路径中的 API Key 会自动用于鉴权。
用户生成的 API Key(sk-gw-*)除了调用网关端点外,还可以调用以下用户自助接口:
curl http://localhost:3000/api/user/quota \
-H 'Authorization: Bearer sk-gw-xxxx'返回示例:
{
"total": {
"quota_requests": 10000,
"quota_tokens": 5000000,
"used_requests": 128,
"used_tokens": 45200,
"remaining_requests": 9872,
"remaining_tokens": 4954800
},
"period": {
"period_seconds": 86400,
"period_label": "每日",
"quota_requests": 1000,
"quota_tokens": 500000,
"used_requests": 12,
"used_tokens": 3200,
"remaining_requests": 988,
"remaining_tokens": 496800,
"reset_at": "2026-05-08 00:00:00"
},
"rate": {
"rpm": 60,
"qps": 5,
"tpm": 100000
}
}
total为终身配额,period为周期性配额(到期自动重置),rate为实时限流参数。值为null或-1表示无限制。
# 查看所有密钥
curl http://localhost:3000/api/user/keys \
-H 'Authorization: Bearer sk-gw-xxxx'
# 创建密钥
curl -X POST http://localhost:3000/api/user/keys \
-H 'Authorization: Bearer sk-gw-xxxx' \
-H 'Content-Type: application/json' \
-d '{}'
# 启用/禁用密钥
curl -X PUT http://localhost:3000/api/user/keys/{id} \
-H 'Authorization: Bearer sk-gw-xxxx' \
-H 'Content-Type: application/json' \
-d '{"enabled": false}'
# 删除密钥
curl -X DELETE http://localhost:3000/api/user/keys/{id} \
-H 'Authorization: Bearer sk-gw-xxxx'curl 'http://localhost:3000/api/user/logs/chat?limit=50&offset=0' \
-H 'Authorization: Bearer sk-gw-xxxx'返回分页日志列表,包含模型、渠道、状态码、Token 用量、延迟等信息,以及汇总统计。
每次调用网关端点(/api/v1/*)时,响应头中会包含当前配额剩余:
X-Quota-Limit-Requests-Remaining: 9872
X-Quota-Limit-Tokens-Remaining: 4954800
X-Period-Quota-Requests-Remaining: 988
X-Period-Quota-Tokens-Remaining: 496800
X-Period-Quota-Reset: 2026-05-08T00:00:00.000Z
精简版不会返回 X-Period-* 周期配额响应头。
- 在管理后台 → API 接口管理中添加渠道(上游地址 + API Key)
- 在管理后台 → 模型管理中创建模型别名映射
- 创建用户和 API Key(或让用户自行注册并创建密钥)
- 调用网关:
# 获取模型列表
curl http://localhost:3000/api/v1/models \
-H 'Authorization: Bearer sk-gw-xxxx'
# Ollama 模型列表
curl http://localhost:3000/api/ollama/api/tags \
-H 'Authorization: Bearer sk-gw-xxxx'
# Ollama 模型列表(query 鉴权)
curl 'http://localhost:3000/api/ollama/api/tags?token=sk-gw-xxxx'
# Ollama 客户端根地址(无法设置鉴权请求头时)
http://localhost:3000/api/ollama/sk-gw-xxxx
# OpenAI Chat Completions
curl http://localhost:3000/api/v1/chat/completions \
-H 'Authorization: Bearer sk-gw-xxxx' \
-H 'Content-Type: application/json' \
-d '{"model": "gpt-4o", "messages": [{"role": "user", "content": "你好"}]}'
# Ollama Chat
curl http://localhost:3000/api/ollama/api/chat \
-H 'Authorization: Bearer sk-gw-xxxx' \
-H 'Content-Type: application/json' \
-d '{"model": "gpt-4o", "messages": [{"role": "user", "content": "你好"}], "stream": false}'
# OpenAI Responses
curl http://localhost:3000/api/v1/responses \
-H 'Authorization: Bearer sk-gw-xxxx' \
-H 'Content-Type: application/json' \
-d '{"model": "gpt-4o", "input": "你好"}'
# Anthropic Claude Messages
curl http://localhost:3000/api/v1/messages \
-H 'Authorization: Bearer sk-gw-xxxx' \
-H 'Content-Type: application/json' \
-d '{"model": "claude-sonnet-4-6", "max_tokens": 1024, "messages": [{"role": "user", "content": "你好"}]}'
# OpenAI Embeddings
curl http://localhost:3000/api/v1/embeddings \
-H 'Authorization: Bearer sk-gw-xxxx' \
-H 'Content-Type: application/json' \
-d '{"model": "text-embedding-3-small", "input": "你好"}'- 渠道声明支持的上游协议,每个模型映射指定使用哪种上游协议。
- 客户端可调用 Chat Completions、Ollama Chat、Responses、Claude Messages 生成端点中的任意一种,网关会在入站协议与上游协议不一致时自动转换;Embeddings 仅直通
/embeddings,不参与协议转换。 - 限流为单实例内存实现,不支持多实例部署。
- 全部采用软删除,记录通过
deleted_at标记而非物理删除。