diff --git a/.gitignore b/.gitignore index aaadf73..13cd9c0 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,4 @@ go.work.sum # Editor/IDE # .idea/ # .vscode/ +ez-api diff --git a/Dockerfile b/Dockerfile index 1f0dd54..13769b8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,17 @@ FROM golang:1.24-alpine AS builder +# Install make for Makefile support +RUN apk add --no-cache make + WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . -RUN go build -o ez-api ./cmd/server + +# Use Makefile to generate swagger and build +RUN make build FROM alpine:latest diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3439c0e --- /dev/null +++ b/Makefile @@ -0,0 +1,58 @@ +.PHONY: all build swagger test clean dev + +# Default target +all: swagger build + +# Generate swagger documentation using go run (no install needed) +swagger: + @echo "Generating Swagger documentation..." + go run github.com/swaggo/swag/cmd/swag@latest init -g cmd/server/main.go -o docs --parseDependency --parseInternal + +# Build the binary +build: swagger + @echo "Building ez-api..." + go build -o ez-api ./cmd/server + +# Build without swagger regeneration (for quick iteration) +build-fast: + go build -o ez-api ./cmd/server + +# Run tests +test: + go test -v ./... + +# Clean build artifacts +clean: + rm -f ez-api + rm -rf docs/ + +# Run in development mode +dev: swagger + go run ./cmd/server + +# Format code +fmt: + go fmt ./... + +# Lint code +lint: + @which golangci-lint > /dev/null || go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest + golangci-lint run + +# Generate swagger only (alias) +docs: swagger + +# Help +help: + @echo "Available targets:" + @echo " all - Generate swagger docs and build (default)" + @echo " build - Generate swagger and build binary" + @echo " build-fast - Build binary without swagger regeneration" + @echo " swagger - Generate Swagger documentation" + @echo " docs - Alias for swagger" + @echo " test - Run tests" + @echo " dev - Run in development mode" + @echo " clean - Remove build artifacts" + @echo " fmt - Format code" + @echo " lint - Run linter" + @echo " help - Show this help" \ No newline at end of file diff --git a/docs/api.md b/docs/api.md index 0c1c5d1..3ce0eb7 100644 --- a/docs/api.md +++ b/docs/api.md @@ -17,8 +17,30 @@ | :--- | :--- | :--- | :--- | | **Admin** | `Authorization: Bearer ` | 环境变量 `EZ_ADMIN_TOKEN` | 全局管理权限,可管理所有租户、供应商和模型。 | | **Master** | `Authorization: Bearer ` | 创建 Master 时返回 | 租户级权限,仅能管理所属的子 Key、查看自身统计和日志。 | +| **Key** | `Authorization: Bearer ` | Master 或 Admin 签发 | 用于调用 AI API,可通过 `/auth/whoami` 查询身份信息。 | | **Internal** | `X-Internal-Token: ` | 环境变量 `EZ_INTERNAL_STATS_TOKEN` | 内部组件(如 Data Plane)同步指标使用。 | +### 1.4 身份识别接口 +使用 `GET /auth/whoami` 可根据 Authorization header 中的 Token 识别当前身份类型: + +| Token 类型 | 返回 `type` | 说明 | +| :--- | :--- | :--- | +| Admin Token | `"admin"` | 进入管理员面板 | +| Master Key | `"master"` | 进入租户自服务面板 | +| Child Key | `"key"` | 显示 Key 信息页,包含 `issued_by` 字段标识签发者 | + +**示例响应**: +```json +// Admin Token +{"type": "admin", "role": "admin"} + +// Master Key +{"type": "master", "id": 1, "name": "研发团队", "group": "default", ...} + +// Child Key +{"type": "key", "id": 5, "master_id": 1, "issued_by": "master", ...} +``` + ### 1.3 通用约定 - **分页处理**: - 管理端列表 (`GET /admin/*`):使用 `page` (从 1 开始) 和 `limit` (默认 50,最大 200)。 @@ -106,6 +128,9 @@ graph TD ## 4. API 模块概览 +### 4.0 公开接口 (Auth API) - 无需中间件 +- **身份识别**:`GET /auth/whoami` - 根据 Token 返回身份类型和详细信息。 + ### 4.1 管理端 (Admin API) - 需 Admin Token - **租户管理**:创建 Master、签发子 Key、实时 QPS 监控、冻结/解冻。 - **上游管理**:ProviderGroup + APIKey 的 CRUD 与批量操作。 diff --git a/docs/docs.go b/docs/docs.go index a8b2a75..489bf98 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -2859,6 +2859,40 @@ const docTemplate = `{ } } }, + "/auth/whoami": { + "get": { + "security": [ + { + "AdminAuth": [] + }, + { + "MasterAuth": [] + } + ], + "description": "Returns the identity of the authenticated user based on the Authorization header.\nSupports Admin Token, Master Key, and Child Key (API Key) authentication.\n\nResponse varies by token type:\n- Admin Token: {\"type\": \"admin\", \"role\": \"admin\"}\n- Master Key: {\"type\": \"master\", \"id\": 1, \"name\": \"...\", ...}\n- Child Key: {\"type\": \"key\", \"id\": 5, \"master_id\": 1, \"issued_by\": \"master\", ...}", + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Get current identity", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/internal_api.WhoamiResponse" + } + }, + "401": { + "description": "Invalid or missing token", + "schema": { + "$ref": "#/definitions/gin.H" + } + } + } + } + }, "/internal/stats/flush": { "post": { "description": "Internal endpoint for flushing accumulated key usage stats from DP to CP database", @@ -4620,6 +4654,83 @@ const docTemplate = `{ } } }, + "internal_api.WhoamiResponse": { + "type": "object", + "properties": { + "created_at": { + "type": "integer", + "example": 1703505600 + }, + "default_namespace": { + "type": "string", + "example": "default" + }, + "epoch": { + "type": "integer", + "example": 1 + }, + "global_qps": { + "type": "integer", + "example": 100 + }, + "group": { + "type": "string", + "example": "default" + }, + "id": { + "description": "Master fields (only present when type is \"master\")", + "type": "integer", + "example": 1 + }, + "issued_at_epoch": { + "type": "integer", + "example": 1 + }, + "issued_by": { + "type": "string", + "example": "master" + }, + "master_id": { + "description": "Key fields (only present when type is \"key\")", + "type": "integer", + "example": 1 + }, + "max_child_keys": { + "type": "integer", + "example": 5 + }, + "name": { + "type": "string", + "example": "tenant-a" + }, + "namespaces": { + "type": "string", + "example": "default,ns1" + }, + "role": { + "description": "Admin fields (only present when type is \"admin\")", + "type": "string", + "example": "admin" + }, + "scopes": { + "type": "string", + "example": "chat:write" + }, + "status": { + "type": "string", + "example": "active" + }, + "type": { + "description": "Type of the authenticated identity: \"admin\", \"master\", or \"key\"", + "type": "string", + "example": "master" + }, + "updated_at": { + "type": "integer", + "example": 1703505600 + } + } + }, "internal_api.refreshModelRegistryRequest": { "type": "object", "properties": { diff --git a/docs/swagger.json b/docs/swagger.json index 75cea0a..cda71cc 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -2853,6 +2853,40 @@ } } }, + "/auth/whoami": { + "get": { + "security": [ + { + "AdminAuth": [] + }, + { + "MasterAuth": [] + } + ], + "description": "Returns the identity of the authenticated user based on the Authorization header.\nSupports Admin Token, Master Key, and Child Key (API Key) authentication.\n\nResponse varies by token type:\n- Admin Token: {\"type\": \"admin\", \"role\": \"admin\"}\n- Master Key: {\"type\": \"master\", \"id\": 1, \"name\": \"...\", ...}\n- Child Key: {\"type\": \"key\", \"id\": 5, \"master_id\": 1, \"issued_by\": \"master\", ...}", + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Get current identity", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/internal_api.WhoamiResponse" + } + }, + "401": { + "description": "Invalid or missing token", + "schema": { + "$ref": "#/definitions/gin.H" + } + } + } + } + }, "/internal/stats/flush": { "post": { "description": "Internal endpoint for flushing accumulated key usage stats from DP to CP database", @@ -4614,6 +4648,83 @@ } } }, + "internal_api.WhoamiResponse": { + "type": "object", + "properties": { + "created_at": { + "type": "integer", + "example": 1703505600 + }, + "default_namespace": { + "type": "string", + "example": "default" + }, + "epoch": { + "type": "integer", + "example": 1 + }, + "global_qps": { + "type": "integer", + "example": 100 + }, + "group": { + "type": "string", + "example": "default" + }, + "id": { + "description": "Master fields (only present when type is \"master\")", + "type": "integer", + "example": 1 + }, + "issued_at_epoch": { + "type": "integer", + "example": 1 + }, + "issued_by": { + "type": "string", + "example": "master" + }, + "master_id": { + "description": "Key fields (only present when type is \"key\")", + "type": "integer", + "example": 1 + }, + "max_child_keys": { + "type": "integer", + "example": 5 + }, + "name": { + "type": "string", + "example": "tenant-a" + }, + "namespaces": { + "type": "string", + "example": "default,ns1" + }, + "role": { + "description": "Admin fields (only present when type is \"admin\")", + "type": "string", + "example": "admin" + }, + "scopes": { + "type": "string", + "example": "chat:write" + }, + "status": { + "type": "string", + "example": "active" + }, + "type": { + "description": "Type of the authenticated identity: \"admin\", \"master\", or \"key\"", + "type": "string", + "example": "master" + }, + "updated_at": { + "type": "integer", + "example": 1703505600 + } + } + }, "internal_api.refreshModelRegistryRequest": { "type": "object", "properties": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 91734c8..1078e4a 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -731,6 +731,64 @@ definitions: description: active/suspended type: string type: object + internal_api.WhoamiResponse: + properties: + created_at: + example: 1703505600 + type: integer + default_namespace: + example: default + type: string + epoch: + example: 1 + type: integer + global_qps: + example: 100 + type: integer + group: + example: default + type: string + id: + description: Master fields (only present when type is "master") + example: 1 + type: integer + issued_at_epoch: + example: 1 + type: integer + issued_by: + example: master + type: string + master_id: + description: Key fields (only present when type is "key") + example: 1 + type: integer + max_child_keys: + example: 5 + type: integer + name: + example: tenant-a + type: string + namespaces: + example: default,ns1 + type: string + role: + description: Admin fields (only present when type is "admin") + example: admin + type: string + scopes: + example: chat:write + type: string + status: + example: active + type: string + type: + description: 'Type of the authenticated identity: "admin", "master", or "key"' + example: master + type: string + updated_at: + example: 1703505600 + type: integer + type: object internal_api.refreshModelRegistryRequest: properties: ref: @@ -2612,6 +2670,33 @@ paths: summary: Test dependency connectivity tags: - system + /auth/whoami: + get: + description: |- + Returns the identity of the authenticated user based on the Authorization header. + Supports Admin Token, Master Key, and Child Key (API Key) authentication. + + Response varies by token type: + - Admin Token: {"type": "admin", "role": "admin"} + - Master Key: {"type": "master", "id": 1, "name": "...", ...} + - Child Key: {"type": "key", "id": 5, "master_id": 1, "issued_by": "master", ...} + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/internal_api.WhoamiResponse' + "401": + description: Invalid or missing token + schema: + $ref: '#/definitions/gin.H' + security: + - AdminAuth: [] + - MasterAuth: [] + summary: Get current identity + tags: + - auth /internal/stats/flush: post: consumes: