# EZ-API (控制平面)

EZ-API 网关系统的管理中心和控制平面。

## 目标

EZ-API 是“控制平面”，负责管理事实来源 (Source of Truth)。
它主要承担：
1. **资源管理**：Master/Key/ProviderGroup/APIKey/Model/Binding/Namespace 的 CRUD 与批量操作。
2. **状态同步**：将配置快照与 Feature Flags 推送到 Redis，供 Balancer 使用。
3. **日志与统计**：日志摄取/统计、操作日志、实时计数回写。
4. **运行配置**：CORS、模型注册表、配额重置等运维能力。

## 架构设计

控制平面采用三层结构，并主动将状态推送到 Redis。

- **数据库**: PostgreSQL（主库），可选独立日志库（`EZ_LOG_PG_DSN`）。
- **缓存/总线**: Redis（配置快照、实时计数、Feature Flags）。
- **框架**: Gin + GORM。

## API 端点

> 补充文档（中文）：[`docs/api.md`](docs/api.md)

### 基础
- `GET /health`
- `GET /api/status/test`
- `GET /swagger/*any`

### Internal
- `POST /internal/stats/flush`：Balancer 统计回写。
- `GET /internal/metrics`：内部指标。

### Admin
- `/admin/masters`（CRUD + batch + manage）
- `/admin/masters/:id/realtime`
- `/admin/masters/:id/keys`
- `/admin/provider-groups`（CRUD）
- `/admin/api-keys`（CRUD + batch）
- `/admin/models`（CRUD + batch）
- `/admin/bindings`（CRUD + batch）
- `/admin/namespaces`（CRUD）
- `/admin/logs`、`/admin/logs/stats`、`/admin/logs/webhook`
- `/admin/features`、`/admin/operation-logs`、`/admin/stats`

### Master
- `/v1/self`
- `/v1/tokens`（CRUD）
- `/v1/logs`、`/v1/logs/stats`
- `/v1/stats`、`/v1/realtime`

### 说明
- Master 的 `global_qps` 默认 `0` 表示限流关闭；仅当 `global_qps > 0` 才会在数据面生效。

## 配置说明

| 变量名 | 默认值 | 说明 |
|--------|--------|------|
| `EZ_API_PORT` | `8080` | 监听端口。 |
| `EZ_PG_DSN` | `host=localhost...` | PostgreSQL 主库 DSN。 |
| `EZ_LOG_PG_DSN` | `` | 日志库 DSN（可选）。 |
| `EZ_REDIS_ADDR` | `localhost:6379` | Redis 地址。 |
| `EZ_REDIS_PASSWORD` | `` | Redis 密码。 |
| `EZ_REDIS_DB` | `0` | Redis DB。 |
| `EZ_CORS_ALLOW_ORIGINS` | `*` | 允许的 Origin 列表（逗号分隔）。 |
| `EZ_LOG_QUEUE` | `10000` | 日志缓冲队列容量。 |
| `EZ_LOG_BATCH_SIZE` | `10` | 单次 DB 写入日志数。 |
| `EZ_LOG_FLUSH_MS` | `1000` | 日志批量刷新间隔（毫秒）。 |
| `EZ_LOG_RETENTION_DAYS` | `30` | 日志保留天数。 |
| `EZ_LOG_MAX_RECORDS` | `1000000` | 日志最大记录数。 |
| `EZ_LOG_PARTITIONING` | `off` | 日志分区（off/month/day）。 |
| `EZ_SYNC_OUTBOX_ENABLED` | `true` | CP->Redis 同步失败时启用 outbox 重试。 |
| `EZ_SYNC_OUTBOX_INTERVAL_SECONDS` | `5` | outbox 重试间隔（秒）。 |
| `EZ_SYNC_OUTBOX_BATCH_SIZE` | `200` | outbox 单次处理数量。 |
| `EZ_SYNC_OUTBOX_MAX_RETRIES` | `10` | outbox 最大重试次数。 |
| `EZ_MODEL_REGISTRY_ENABLED` | `false` | 模型注册表开关。 |
| `EZ_MODEL_REGISTRY_REFRESH_SECONDS` | `1800` | 模型注册表刷新间隔。 |
| `EZ_MODEL_REGISTRY_CACHE_DIR` | `./data/model-registry` | 模型注册表缓存目录。 |
| `EZ_QUOTA_RESET_INTERVAL_SECONDS` | `300` | 配额重置间隔。 |
| `EZ_INTERNAL_STATS_TOKEN` | `` | 内部统计回写鉴权 token。 |

配置读取优先级：环境变量 > 配置文件 > 默认值。通过 [Viper](https://github.com/spf13/viper) 支持 `./config.yaml`（或 `./config/config.yaml`），也可用 `EZ_CONFIG_FILE` 指定路径。示例：

```yaml
server:
  port: 8080
postgres:
  dsn: host=localhost user=postgres password=postgres dbname=ezapi port=5432 sslmode=disable
redis:
  addr: localhost:6379
  db: 0
log:
  batch_size: 10
  flush_ms: 1000
  queue_capacity: 10000
auth:
  jwt_secret: change_me_in_production
```

## 运行方式

### 本地运行

运行前需要准备依赖（Postgres + Redis），否则会出现连接错误

建议先启动基础依赖：

```bash
docker compose up -d postgres redis
```

然后在本地设置必要环境变量（可基于 `.env.example`）：

```bash
go run cmd/server/main.go
```

### Docker Compose（推荐）

在 `ez-api` 目录使用自带的 `docker-compose.yml` 拉起完整栈（Postgres + 日志库 + Redis + ez-api + balancer）：

```bash
cd ez-api
docker compose up -d
```

常用可选变量（写入 `.env`）：

```env
EZ_ADMIN_TOKEN=admin123
EZ_INTERNAL_STATS_TOKEN=internal123
LOG_POSTGRES_USER=postgres
LOG_POSTGRES_PASSWORD=postgres
LOG_POSTGRES_DB=ezapi_logs
EZ_BALANCER_LOG_SINK_ENABLED=false
EZ_BALANCER_LOG_SINK_BASE_URL=http://ez-api:8080
EZ_BALANCER_STATS_FLUSH_ENABLED=false
EZ_BALANCER_STATS_FLUSH_BASE_URL=http://ez-api:8080
EZ_BALANCER_STATS_FLUSH_TOKEN=internal123
```

### Docker 单独运行

```bash
docker build -t ez-api .
docker run -p 8080:8080 --env-file .env ez-api
```

### 本地联合开发（配合 balancer）

- 目录结构建议：`/workspace/` 下并列放置 `ez-api`、`balancer`、`foundation`。
- 初始化本地 Go 工作区（Go 1.20+，不要求提交到任一仓库）：在 `/workspace` 执行
  ```bash
  go work init
  go work use ./ez-api ./balancer ./foundation
  ```
  如果你只使用已发布的 `github.com/ez-api/foundation v0.3.0`（或更高 tag），则不需要 `go.work`。

### 集成测试

依赖 Docker 与 docker compose，且默认在工作区里与 `balancer` 仓库并列（用于构建镜像）。在仓库内运行：

```bash
cd ez-api
./test/integration.sh
```

脚本会拉起 `docker-compose.integration.yml` 中的服务，运行带 `integration` tag 的 Go 测试，并在完成后清理容器和卷。

## 测试

- 单元测试：`go test ./...`（测试文件与源码同目录，更多约定见 `TESTING.md`）

## 日志

- 业务代码统一使用标准库 `log/slog`（`logger.Info("msg", "k", v)` 风格）。
- 输出后端仍为 [zerolog](https://github.com/rs/zerolog)（通过 `github.com/ez-api/foundation/logging` 的 slog handler bridge），默认 ConsoleWriter。
- 通过 `EZ_LOG_LEVEL` 控制控制平面的日志等级，配合异步 DB 写入（LogWriter）一起使用。

## JSON

- 项目内 JSON 编解码统一走 `github.com/ez-api/foundation/jsoncodec`（内部使用 Sonic）。

## 依赖约定

- Control Plane（ez-api）与 Data Plane（balancer）共享一部分“协议约定/基础设施”代码（JSON、日志、provider type 规则），统一沉淀在 `github.com/ez-api/foundation`。
- 本仓库的 `go.mod` 需要依赖一个可用的 `foundation` 版本：发布后建议锁定到 `v0.3.0`（或更高 tag）；本地联调可使用 `go.work`（见下文）。

## 设计决策

- **异步日志**: 日志不会立即写入 DB。它们被缓冲在内存中，并分批刷新，以减少 DB IOPS。
- **快照同步**: Balancer 不查询 DB，而是由 EZ-API 将 JSON 快照推送到 Redis。这将高流量的数据平面与关系型数据库解耦。
