mirror of
https://github.com/EZ-Api/ez-api.git
synced 2026-01-13 09:37:53 +00:00
feat(api): add public status endpoints with version injection
Replace health_handler with status_handler providing public /status and /about endpoints. Add build-time version injection via ldflags in Makefile, and support --version/-v CLI flag. - Add /status endpoint returning runtime status, uptime, and version - Add /about endpoint with system metadata (name, description, repo) - Configure VERSION variable with git describe fallback - Update swagger docs and api.md for new public endpoints - Remove deprecated /api/status/test endpoint
This commit is contained in:
12
Makefile
12
Makefile
@@ -1,5 +1,9 @@
|
||||
.PHONY: all build swagger test clean dev
|
||||
|
||||
# Version injection - can be overridden via make build VERSION=v1.0.0
|
||||
VERSION ?= $(shell git describe --tags --always 2>/dev/null || echo "v0.1.0-dev")
|
||||
LDFLAGS := -X github.com/ez-api/ez-api/internal/api.Version=$(VERSION)
|
||||
|
||||
# Default target
|
||||
all: swagger build
|
||||
|
||||
@@ -10,12 +14,12 @@ swagger:
|
||||
|
||||
# Build the binary
|
||||
build: swagger
|
||||
@echo "Building ez-api..."
|
||||
go build -o ez-api ./cmd/server
|
||||
@echo "Building ez-api $(VERSION)..."
|
||||
go build -ldflags "$(LDFLAGS)" -o ez-api ./cmd/server
|
||||
|
||||
# Build without swagger regeneration (for quick iteration)
|
||||
build-fast:
|
||||
go build -o ez-api ./cmd/server
|
||||
go build -ldflags "$(LDFLAGS)" -o ez-api ./cmd/server
|
||||
|
||||
# Run tests
|
||||
test:
|
||||
@@ -28,7 +32,7 @@ clean:
|
||||
|
||||
# Run in development mode
|
||||
dev: swagger
|
||||
go run ./cmd/server
|
||||
go run -ldflags "$(LDFLAGS)" ./cmd/server
|
||||
|
||||
# Format code
|
||||
fmt:
|
||||
|
||||
@@ -77,6 +77,12 @@ func isOriginAllowed(allowed []string, origin string) bool {
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Handle --version flag before any initialization
|
||||
if len(os.Args) > 1 && (os.Args[1] == "--version" || os.Args[1] == "-v") {
|
||||
fmt.Printf("ez-api %s\n", api.Version)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
logger, _ := logging.New(logging.Options{Service: "ez-api"})
|
||||
if len(os.Args) > 1 && os.Args[1] == "import" {
|
||||
code := runImport(logger, os.Args[2:])
|
||||
@@ -196,7 +202,7 @@ func main() {
|
||||
masterService := service.NewMasterService(db)
|
||||
statsService := service.NewStatsService(rdb)
|
||||
healthService := service.NewHealthCheckService(db, rdb)
|
||||
healthHandler := api.NewHealthHandler(healthService)
|
||||
statusHandler := api.NewStatusHandler(healthService)
|
||||
|
||||
handler := api.NewHandler(db, logDB, syncService, logWriter, rdb, logPartitioner)
|
||||
adminHandler := api.NewAdminHandler(db, logDB, masterService, syncService, statsService, logPartitioner)
|
||||
@@ -265,7 +271,10 @@ func main() {
|
||||
}
|
||||
c.JSON(httpStatus, status)
|
||||
})
|
||||
r.GET("/api/status/test", healthHandler.TestDeps)
|
||||
|
||||
// Public Status Endpoints
|
||||
r.GET("/status", statusHandler.Status)
|
||||
r.GET("/about", statusHandler.About)
|
||||
|
||||
// Swagger Documentation
|
||||
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
|
||||
|
||||
10
docs/api.md
10
docs/api.md
@@ -128,8 +128,14 @@ graph TD
|
||||
|
||||
## 4. API 模块概览
|
||||
|
||||
### 4.0 公开接口 (Auth API) - 无需中间件
|
||||
- **身份识别**:`GET /auth/whoami` - 根据 Token 返回身份类型和详细信息。
|
||||
### 4.0 公开接口 - 无需鉴权
|
||||
| 端点 | 说明 | 响应示例 |
|
||||
|------|------|----------|
|
||||
| `GET /health` | 健康检查(含依赖状态) | `{"status": "ok", "database": "up", "redis": "up", "uptime": "1h30m"}` |
|
||||
| `GET /status` | 公开状态摘要 | `{"status": "ok", "uptime": "1h30m", "version": "v0.1.0"}` |
|
||||
| `GET /about` | 系统信息 | `{"name": "EZ-API Gateway", "version": "v0.1.0", ...}` |
|
||||
| `GET /auth/whoami` | 身份识别 | 根据 Token 返回身份类型和详细信息 |
|
||||
| `GET /swagger/*` | API 文档 | Swagger UI |
|
||||
|
||||
### 4.1 管理端 (Admin API) - 需 Admin Token
|
||||
- **租户管理**:创建 Master、签发子 Key、实时 QPS 监控、冻结/解冻。
|
||||
|
||||
121
docs/docs.go
121
docs/docs.go
@@ -24,6 +24,26 @@ const docTemplate = `{
|
||||
"host": "{{.Host}}",
|
||||
"basePath": "{{.BasePath}}",
|
||||
"paths": {
|
||||
"/about": {
|
||||
"get": {
|
||||
"description": "Returns system metadata for display on an about page",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Public"
|
||||
],
|
||||
"summary": "Get system information",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/internal_api.AboutResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/admin/api-keys": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -2844,32 +2864,6 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/status/test": {
|
||||
"get": {
|
||||
"description": "Checks Redis/PostgreSQL connections and reports status",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"system"
|
||||
],
|
||||
"summary": "Test dependency connectivity",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/github_com_ez-api_ez-api_internal_service.HealthStatus"
|
||||
}
|
||||
},
|
||||
"503": {
|
||||
"description": "Service Unavailable",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/github_com_ez-api_ez-api_internal_service.HealthStatus"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/auth/whoami": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -2990,6 +2984,26 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/status": {
|
||||
"get": {
|
||||
"description": "Returns public runtime status information without sensitive data",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Public"
|
||||
],
|
||||
"summary": "Get system status",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/internal_api.StatusResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/logs": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -3925,23 +3939,6 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"github_com_ez-api_ez-api_internal_service.HealthStatus": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"database": {
|
||||
"type": "string"
|
||||
},
|
||||
"redis": {
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"type": "string"
|
||||
},
|
||||
"uptime": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"github_com_ez-api_ez-api_internal_service.LogWebhookConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -4049,6 +4046,27 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"internal_api.AboutResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string",
|
||||
"example": "High-performance LLM API gateway"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"example": "EZ-API Gateway"
|
||||
},
|
||||
"repository": {
|
||||
"type": "string",
|
||||
"example": "https://github.com/ez-api/ez-api"
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"example": "0.1.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"internal_api.AccessResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -4635,6 +4653,23 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"internal_api.StatusResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status": {
|
||||
"type": "string",
|
||||
"example": "ok"
|
||||
},
|
||||
"uptime": {
|
||||
"type": "string",
|
||||
"example": "72h30m15s"
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"example": "0.1.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"internal_api.TokenView": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -18,6 +18,26 @@
|
||||
"host": "localhost:8080",
|
||||
"basePath": "/",
|
||||
"paths": {
|
||||
"/about": {
|
||||
"get": {
|
||||
"description": "Returns system metadata for display on an about page",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Public"
|
||||
],
|
||||
"summary": "Get system information",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/internal_api.AboutResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/admin/api-keys": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -2838,32 +2858,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/status/test": {
|
||||
"get": {
|
||||
"description": "Checks Redis/PostgreSQL connections and reports status",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"system"
|
||||
],
|
||||
"summary": "Test dependency connectivity",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/github_com_ez-api_ez-api_internal_service.HealthStatus"
|
||||
}
|
||||
},
|
||||
"503": {
|
||||
"description": "Service Unavailable",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/github_com_ez-api_ez-api_internal_service.HealthStatus"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/auth/whoami": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -2984,6 +2978,26 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/status": {
|
||||
"get": {
|
||||
"description": "Returns public runtime status information without sensitive data",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Public"
|
||||
],
|
||||
"summary": "Get system status",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/internal_api.StatusResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/logs": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -3919,23 +3933,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"github_com_ez-api_ez-api_internal_service.HealthStatus": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"database": {
|
||||
"type": "string"
|
||||
},
|
||||
"redis": {
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"type": "string"
|
||||
},
|
||||
"uptime": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"github_com_ez-api_ez-api_internal_service.LogWebhookConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -4043,6 +4040,27 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"internal_api.AboutResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string",
|
||||
"example": "High-performance LLM API gateway"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"example": "EZ-API Gateway"
|
||||
},
|
||||
"repository": {
|
||||
"type": "string",
|
||||
"example": "https://github.com/ez-api/ez-api"
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"example": "0.1.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"internal_api.AccessResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -4629,6 +4647,23 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"internal_api.StatusResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status": {
|
||||
"type": "string",
|
||||
"example": "ok"
|
||||
},
|
||||
"uptime": {
|
||||
"type": "string",
|
||||
"example": "72h30m15s"
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"example": "0.1.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"internal_api.TokenView": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -247,17 +247,6 @@ definitions:
|
||||
updatedAt:
|
||||
type: string
|
||||
type: object
|
||||
github_com_ez-api_ez-api_internal_service.HealthStatus:
|
||||
properties:
|
||||
database:
|
||||
type: string
|
||||
redis:
|
||||
type: string
|
||||
status:
|
||||
type: string
|
||||
uptime:
|
||||
type: string
|
||||
type: object
|
||||
github_com_ez-api_ez-api_internal_service.LogWebhookConfig:
|
||||
properties:
|
||||
cooldown_seconds:
|
||||
@@ -328,6 +317,21 @@ definitions:
|
||||
description: Valid is true if Time is not NULL
|
||||
type: boolean
|
||||
type: object
|
||||
internal_api.AboutResponse:
|
||||
properties:
|
||||
description:
|
||||
example: High-performance LLM API gateway
|
||||
type: string
|
||||
name:
|
||||
example: EZ-API Gateway
|
||||
type: string
|
||||
repository:
|
||||
example: https://github.com/ez-api/ez-api
|
||||
type: string
|
||||
version:
|
||||
example: 0.1.0
|
||||
type: string
|
||||
type: object
|
||||
internal_api.AccessResponse:
|
||||
properties:
|
||||
default_namespace:
|
||||
@@ -712,6 +716,18 @@ definitions:
|
||||
tokens:
|
||||
type: integer
|
||||
type: object
|
||||
internal_api.StatusResponse:
|
||||
properties:
|
||||
status:
|
||||
example: ok
|
||||
type: string
|
||||
uptime:
|
||||
example: 72h30m15s
|
||||
type: string
|
||||
version:
|
||||
example: 0.1.0
|
||||
type: string
|
||||
type: object
|
||||
internal_api.TokenView:
|
||||
properties:
|
||||
allow_ips:
|
||||
@@ -943,6 +959,19 @@ info:
|
||||
title: EZ-API Control Plane
|
||||
version: 0.0.1
|
||||
paths:
|
||||
/about:
|
||||
get:
|
||||
description: Returns system metadata for display on an about page
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/internal_api.AboutResponse'
|
||||
summary: Get system information
|
||||
tags:
|
||||
- Public
|
||||
/admin/api-keys:
|
||||
get:
|
||||
description: List API keys
|
||||
@@ -2744,23 +2773,6 @@ paths:
|
||||
summary: Force sync snapshot
|
||||
tags:
|
||||
- admin
|
||||
/api/status/test:
|
||||
get:
|
||||
description: Checks Redis/PostgreSQL connections and reports status
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/github_com_ez-api_ez-api_internal_service.HealthStatus'
|
||||
"503":
|
||||
description: Service Unavailable
|
||||
schema:
|
||||
$ref: '#/definitions/github_com_ez-api_ez-api_internal_service.HealthStatus'
|
||||
summary: Test dependency connectivity
|
||||
tags:
|
||||
- system
|
||||
/auth/whoami:
|
||||
get:
|
||||
description: |-
|
||||
@@ -2845,6 +2857,19 @@ paths:
|
||||
summary: Ingest logs
|
||||
tags:
|
||||
- system
|
||||
/status:
|
||||
get:
|
||||
description: Returns public runtime status information without sensitive data
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/internal_api.StatusResponse'
|
||||
summary: Get system status
|
||||
tags:
|
||||
- Public
|
||||
/v1/logs:
|
||||
get:
|
||||
description: List request logs for the authenticated master
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/ez-api/ez-api/internal/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type HealthHandler struct {
|
||||
svc *service.HealthCheckService
|
||||
}
|
||||
|
||||
func NewHealthHandler(svc *service.HealthCheckService) *HealthHandler {
|
||||
return &HealthHandler{svc: svc}
|
||||
}
|
||||
|
||||
// TestDeps godoc
|
||||
// @Summary Test dependency connectivity
|
||||
// @Description Checks Redis/PostgreSQL connections and reports status
|
||||
// @Tags system
|
||||
// @Produce json
|
||||
// @Success 200 {object} service.HealthStatus
|
||||
// @Failure 503 {object} service.HealthStatus
|
||||
// @Router /api/status/test [get]
|
||||
func (h *HealthHandler) TestDeps(c *gin.Context) {
|
||||
if h == nil || h.svc == nil {
|
||||
c.JSON(http.StatusServiceUnavailable, gin.H{"status": "down"})
|
||||
return
|
||||
}
|
||||
status := h.svc.Check(c.Request.Context())
|
||||
httpStatus := http.StatusOK
|
||||
if status.Status == "down" {
|
||||
httpStatus = http.StatusServiceUnavailable
|
||||
}
|
||||
c.JSON(httpStatus, status)
|
||||
}
|
||||
81
internal/api/status_handler.go
Normal file
81
internal/api/status_handler.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/ez-api/ez-api/internal/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// Version is set at build time using ldflags
|
||||
// e.g., go build -ldflags "-X github.com/ez-api/ez-api/internal/api.Version=v1.0.0"
|
||||
var Version = "v0.1.0-dev"
|
||||
|
||||
// startTime is used to calculate uptime
|
||||
var startTime = time.Now()
|
||||
|
||||
// StatusHandler handles public status endpoints
|
||||
type StatusHandler struct {
|
||||
healthService *service.HealthCheckService
|
||||
}
|
||||
|
||||
// NewStatusHandler creates a new StatusHandler
|
||||
func NewStatusHandler(healthService *service.HealthCheckService) *StatusHandler {
|
||||
return &StatusHandler{
|
||||
healthService: healthService,
|
||||
}
|
||||
}
|
||||
|
||||
// StatusResponse represents the response for /status endpoint
|
||||
type StatusResponse struct {
|
||||
Status string `json:"status" example:"ok"`
|
||||
Uptime string `json:"uptime" example:"72h30m15s"`
|
||||
Version string `json:"version" example:"0.1.0"`
|
||||
}
|
||||
|
||||
// AboutResponse represents the response for /about endpoint
|
||||
type AboutResponse struct {
|
||||
Name string `json:"name" example:"EZ-API Gateway"`
|
||||
Version string `json:"version" example:"0.1.0"`
|
||||
Description string `json:"description" example:"High-performance LLM API gateway"`
|
||||
Repository string `json:"repository" example:"https://github.com/ez-api/ez-api"`
|
||||
}
|
||||
|
||||
// Status godoc
|
||||
// @Summary Get system status
|
||||
// @Description Returns public runtime status information without sensitive data
|
||||
// @Tags Public
|
||||
// @Produce json
|
||||
// @Success 200 {object} StatusResponse
|
||||
// @Router /status [get]
|
||||
func (h *StatusHandler) Status(c *gin.Context) {
|
||||
// Check health status
|
||||
health := h.healthService.Check(c.Request.Context())
|
||||
|
||||
resp := StatusResponse{
|
||||
Status: health.Status,
|
||||
Uptime: time.Since(startTime).Round(time.Second).String(),
|
||||
Version: Version,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
// About godoc
|
||||
// @Summary Get system information
|
||||
// @Description Returns system metadata for display on an about page
|
||||
// @Tags Public
|
||||
// @Produce json
|
||||
// @Success 200 {object} AboutResponse
|
||||
// @Router /about [get]
|
||||
func (h *StatusHandler) About(c *gin.Context) {
|
||||
resp := AboutResponse{
|
||||
Name: "EZ-API Gateway",
|
||||
Version: Version,
|
||||
Description: "High-performance LLM API gateway",
|
||||
Repository: "https://github.com/ez-api/ez-api",
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
Reference in New Issue
Block a user