# EasyAI AI Gateway

独立的 AI 网关中台脚手架，用于把现有 `integration-platform` 的平台管理、模型路由、计费预估、队列执行、Chat / 生图 / 生视频等生成能力逐步从 `easyai-server-main` 拆成可独立运行的项目。

## 技术选型

- 后端：Go + PostgreSQL 18，复用 Agent memory 的 `easyai-pgvector`，支持本地用户、可选邀请码、API Key、余额/充值闭环，也支持复用 `server-main` 的 JWT / API Key 授权语义。
- 前端：React + TypeScript + TSX，UI 体系按 `shadcn-ui` / Radix / Tailwind 方向沉淀，先提供运维控制台骨架。
- Monorepo：Nx 负责任务编排，Go 使用 `go.work` 管理模块。
- 集成：完成后由 `easyai-server-main` 通过内部 HTTP SDK 直连本服务；任务实时进度由 Gateway 回调 `server-main`，再通过原 WebSocket 网关推送给业务前端。

## 目录

```text
apps/
  api/        Go HTTP API, auth middleware, PG store, migrations
  web/        React TSX admin console
packages/
  contracts/ Shared TypeScript DTO contracts
docs/
  design.md  Detailed architecture and migration design
```

## 本地启动

```bash
cp .env.example .env
pnpm install
pnpm dev
```

服务默认地址：

- API: `http://localhost:8088`
- Web: `http://localhost:5178`
- PostgreSQL: 目标版本 18，默认使用宿主机 `localhost:5432` 上的 `easyai-pgvector` 实例，并使用独立库 `easyai_ai_gateway`
- 身份模式: 默认 `IDENTITY_MODE=hybrid`，可同时测试 Gateway 本地账号注册登录、可选邀请码和 `server-main` JWT / API Key 对接。

### Auth Center 统一认证

统一认证不再通过 `OIDC_*` 或 `VITE_OIDC_*` 业务环境变量配置。管理员先在 Auth Center 的通用“应用接入”向导选择 OIDC 登录、API 验证、机器调用、Token Introspection 和 SSF 会话撤销等标准能力，再到 Gateway 的“系统设置 → 统一认证”填写 Auth Center 地址、一次性接入码、API/Web 公网地址和本地租户映射。Gateway 自动领取标准 Manifest 与一次性机器凭据，验证通过后热切换，无需重启。

部署环境只提供 SecretStore 等启动级配置：

```dotenv
IDENTITY_SECRET_STORE=file
IDENTITY_SECRET_DIR=.local-secrets/identity
IDENTITY_SECURITY_EVENTS_HEARTBEAT_INTERVAL_SECONDS=60
IDENTITY_SECURITY_EVENTS_STALE_AFTER_SECONDS=180
IDENTITY_SECURITY_EVENTS_CLOCK_SKEW_SECONDS=60
```

OIDC 用户通过签名、Issuer、Audience、`tid`、Scope 和应用角色校验后，Gateway 可按当前 Active Revision 的策略创建本地业务投影。Web Console 使用公共 Client + PKCE + Gateway BFF Session；浏览器 JavaScript 只持有 HttpOnly 随机 Session Cookie。可选 SSF/CAEP 能力自动复用同一机器凭据，推送不健康时降级到 RFC 7662，内省也不可用时 OIDC Fail Closed。完整行为见 [统一认证运行时配置](docs/standard-identity-runtime-configuration.md)、[OIDC JIT 接入说明](docs/oidc-jit-provisioning.md)和 [SSF 会话撤销运行手册](docs/security/ssf-session-revocation.md)。

`pnpm dev` 会先创建数据库并执行 migration，然后并行启动：

- `api:dev`：通过 `scripts/go-watch.mjs` 运行 Go API，监听 `.go`、`go.mod`、`go.sum` 变化并自动重启后端进程；watcher 会按进程组终止旧的 `go run` 和其子进程，避免热更新时残留进程占用 API 端口。
- `web:dev`：Vite React dev server。

后端热更新可通过 `GO_WATCH_SHUTDOWN_GRACE_MS` 和 `GO_WATCH_RESTART_DELAY_MS` 调整旧进程退出等待时间与重启间隔。

## Docker Compose 本地运行

仓库内的 Compose 脚本只用于本地构建和运行：启动 PostgreSQL、执行数据库迁移，并验证 API 与 Web 是否可访问。它不再包含 Registry 推送或生产部署能力：

```bash
scripts/deploy-compose.sh
```

部署成功后默认访问地址：

- Web: `http://127.0.0.1:5178`
- API: `http://127.0.0.1:8088/api/v1/healthz`
- Web 反代公开 API: `http://127.0.0.1:5178/api/v1/healthz`

公开接口统一使用 `/api/v1` 前缀，完整分组清单见 [公开 API V1 清单](docs/public-api-v1.md)。

常用覆盖项：

```bash
AI_GATEWAY_IMAGE_TAG=local-test scripts/deploy-compose.sh
AI_GATEWAY_WEB_PORT=8080 AI_GATEWAY_API_PORT=18088 scripts/deploy-compose.sh
AI_GATEWAY_GO_PROXY='https://proxy.golang.org,direct' scripts/deploy-compose.sh
AI_GATEWAY_NPM_REGISTRY='https://registry.npmmirror.com' scripts/deploy-compose.sh
AI_GATEWAY_SKIP_BUILD=1 scripts/deploy-compose.sh
scripts/deploy-compose.sh down
scripts/deploy-compose.sh clean
```

默认本地镜像地址为：

- API: `registry.cn-shanghai.aliyuncs.com/easyaigc/ai-gateway:local`
- Web: `registry.cn-shanghai.aliyuncs.com/easyaigc/ai-gateway-web:local`

生产镜像禁止使用 `latest`，只能通过后文的人工 publish 命令推送完整 Git SHA Tag。

Web 容器的 Nginx 配置通过 bind mount 挂载自仓库文件 [docker/nginx.conf](docker/nginx.conf)，可直接修改该文件调整静态资源、规范 `/api/v1` 公开入口和旧 `/gateway-api` 兼容反向代理。修改后执行以下命令使配置生效：

```bash
docker compose -f docker-compose.yml restart web
```

## 生产人工发布

本仓库没有 Gitea Actions、Webhook、Tag、`main` Push、轮询或定时发布。`main` 不使用受保护分支限制；commit、push 和 Tag 都不会构建镜像或更新生产。

生产发布由 Agent 在用户明确指令下按两个阶段连续执行。第一步在本机构建 `linux/amd64` 镜像、运行临时 PostgreSQL + simulation API 冒烟、推送完整 SHA Tag，并生成带内容完整性校验的 digest-pinned manifest；该命令不会修改生产：

```bash
docker login --username=<your-aliyun-account> registry.cn-shanghai.aliyuncs.com
./scripts/publish-release-images.sh --components auto
```

用户要求“发布”“上线”或“部署到线上”时，Agent 核验并报告 `dist/releases/<SHA>.json`、组件和 digest 后直接执行第二步，无需再次确认；用户仅要求构建或推送镜像时才在第一步后停止：

```bash
./scripts/deploy-production-release.sh dist/releases/<SHA>.json
```

查看生产版本和显式回滚：

```bash
./scripts/deploy-production-release.sh --status
./scripts/deploy-production-release.sh --rollback <历史完整 Git SHA>
```

完整安装、验证、失败处理和停用旧自动化步骤见[人工生产发布运行手册](docs/runbooks/production-ci-cd.md)；
三节点 K3s、CloudNativePG、双 NGINX、备份恢复和跨节点文件验收见
[K3s 高可用运行手册](docs/operations/k3s-ha-runbook.md)；正式流量接入前的 Gemini
带图、多参考图视频、Worker 强杀和容量阶梯验收见
[生产同构验收手册](docs/operations/production-acceptance.md)。决策背景见
[ADR-003](docs/decisions/003-manual-agent-release.md)。

生产验收前先运行[本地三节点同构验收](docs/operations/local-isomorphic-acceptance.md)；
本地原生高并发、精确 amd64 制品冒烟、线上模拟、真实金丝雀和人工开闸使用同一
`acceptance-report/v1` 身份链路。

Compose 默认使用独立容器数据库 `postgres:18-alpine`，数据卷会保留在 `postgres_data` 和 `api_data`。为避免本地开发 `.env` 中的 `localhost` 数据库地址污染容器部署，compose 使用 `AI_GATEWAY_COMPOSE_*` 变量作为容器部署专用覆盖，例如：

```bash
AI_GATEWAY_COMPOSE_DATABASE_URL='postgresql://easyai:easyai2025@postgres:5432/easyai_ai_gateway?sslmode=disable' scripts/deploy-compose.sh
```

数据库迁移仍通过 `migrator` 容器执行，但脚本使用 `docker compose run --rm migrator`，迁移成功后不会留下 `migrator-1 Exited` 容器。

## OpenAPI 文档

修改 `apps/api/internal/httpapi` 下的接口、请求或响应类型后，请重新执行：

```bash
pnpm openapi
```

中国区可灵 O1 / 3.0 Omni 的 V1 AK/SK 与 API 2.0 兼容接入方式见 [可灵兼容接口说明](docs/kling-compatible-api.md)。


默认 EasyAI 部署里，`easyai-pgvector` 在容器网络内的连接串是：

```dotenv
AI_GATEWAY_DATABASE_URL=postgresql://easyai:easyai2025@easyai-pgvector:5432/easyai_ai_gateway?schema=public
```

宿主机直跑时需要使用宿主机可访问的 Postgres 地址。如果 `easyai-pgvector` 将 `5432` 映射到了本机，可使用：

```dotenv
AI_GATEWAY_DATABASE_URL=postgresql://easyai:easyai2025@localhost:5432/easyai_ai_gateway?sslmode=disable
```

如果现有 `easyai-pgvector` 没有把 `5432` 映射到宿主机，就需要补端口映射，或者把 AI Gateway 后端容器化后接入同一个 `easyai` Docker network。

异步队列 worker 不使用固定业务并发。服务分别汇总启用平台模型和活跃用户组的有效 `concurrent` 策略，采用两者中更严格的集群容量，默认每 5 秒在线调整 River 执行容量。策略解析兼容历史 `platformLimits/modelLimits.max_concurrent_requests`，运行时统一转换为 `rules`。`AI_GATEWAY_ASYNC_WORKER_HARD_LIMIT` 默认 `2048`，限制策略推导出的集群目标；`AI_GATEWAY_ASYNC_WORKER_INSTANCE_HARD_LIMIT` 默认 `32`，按单 Worker 的内存安全容量限制实例分配，即使其他实例失活也不会突破。Worker 的 `AI_GATEWAY_DATABASE_MAX_CONNS` 必须高于实例执行容量，为心跳、选主、租约续期和健康检查保留连接；生产环境按每实例执行容量 `24` 配置连接池上限 `32`。`AI_GATEWAY_DATABASE_MIN_IDLE_CONNS` 控制启动预热连接数，默认 `0`，生产 K3s 配置为 `4`；`AI_GATEWAY_DATABASE_MAX_CONN_IDLE_SECONDS` 可缩短突发连接的空闲回收时间，生产配置为 `30` 秒，避免把连接池高水位长期常驻。平台模型和用户组的 PostgreSQL concurrency lease 仍是业务并发真值。可通过 `AI_GATEWAY_ASYNC_WORKER_REFRESH_INTERVAL_SECONDS` 调整刷新周期。

## 迁移原则

1. 新服务先并行运行，不直接删除 `easyai-server-main` 内现有模块。
2. 身份域支持 `standalone`、`server-main`、`hybrid` 三种模式；独立模式由 Gateway 维护租户、用户、用户组、本地 API Key、余额和充值订单，接入模式从 `server-main` 同步租户、用户和用户组。
3. OpenAPI `sk-*` 校验、文件上传、扣费结算在接入模式下仍由 `server-main` 承担；独立模式走 Gateway 本地闭环。
4. 网关服务负责基准模型库、平台模型路由、用户组调用折扣、TPM/RPM/并发限流、任务队列、三方平台执行、任务进度事件和回调 outbox。
5. 切流时优先让 `server-main` 的 `OpenaiService` 变成薄门面，内部调用本服务。

详细设计见 [docs/design.md](docs/design.md)。
