refactor(api): update traffic chart response structure

Change the traffic chart API response from bucket-based to series-based
to better support frontend visualization libraries. The new format
provides a shared X-axis and aligned data arrays for each model series.

- Replace `buckets` with `x` and `series` in response
- Implement data alignment and zero-filling for time slots
- Update Swagger documentation including pending definitions

BREAKING CHANGE: The `GET /admin/logs/stats/traffic-chart` response
schema has changed. `buckets` and `models` fields are removed.
This commit is contained in:
zenfun
2026-01-08 18:40:44 +08:00
parent 341b54b185
commit f400ffde95
10 changed files with 3239 additions and 294 deletions

View File

@@ -1183,7 +1183,7 @@ const docTemplate = `{
"parameters": [
{
"type": "string",
"description": "time period: today, week, month, all",
"description": "time period: today, week, month, last7d, last30d, all",
"name": "period",
"in": "query"
},
@@ -1198,6 +1198,12 @@ const docTemplate = `{
"description": "unix seconds",
"name": "until",
"in": "query"
},
{
"type": "boolean",
"description": "include trend data comparing to previous period",
"name": "include_trends",
"in": "query"
}
],
"responses": {
@@ -1302,6 +1308,272 @@ const docTemplate = `{
}
}
},
"/admin/ip-bans": {
"get": {
"security": [
{
"AdminAuth": []
}
],
"description": "List all global IP/CIDR ban rules",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"admin",
"ip-bans"
],
"summary": "List IP bans",
"parameters": [
{
"type": "string",
"description": "Filter by status (active, expired)",
"name": "status",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/internal_api.IPBanView"
}
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/gin.H"
}
}
}
},
"post": {
"security": [
{
"AdminAuth": []
}
],
"description": "Create a new global IP/CIDR ban rule",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"admin",
"ip-bans"
],
"summary": "Create an IP ban",
"parameters": [
{
"description": "IP Ban Info",
"name": "ban",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/internal_api.CreateIPBanRequest"
}
}
],
"responses": {
"201": {
"description": "Created",
"schema": {
"$ref": "#/definitions/internal_api.IPBanView"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/gin.H"
}
},
"409": {
"description": "Conflict",
"schema": {
"$ref": "#/definitions/gin.H"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/gin.H"
}
}
}
}
},
"/admin/ip-bans/{id}": {
"get": {
"security": [
{
"AdminAuth": []
}
],
"description": "Get a single global IP/CIDR ban rule by ID",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"admin",
"ip-bans"
],
"summary": "Get an IP ban",
"parameters": [
{
"type": "integer",
"description": "IP Ban ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/internal_api.IPBanView"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/gin.H"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/gin.H"
}
}
}
},
"put": {
"security": [
{
"AdminAuth": []
}
],
"description": "Update a global IP/CIDR ban rule",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"admin",
"ip-bans"
],
"summary": "Update an IP ban",
"parameters": [
{
"type": "integer",
"description": "IP Ban ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "IP Ban Update",
"name": "ban",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/internal_api.UpdateIPBanRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/internal_api.IPBanView"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/gin.H"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/gin.H"
}
},
"409": {
"description": "Conflict",
"schema": {
"$ref": "#/definitions/gin.H"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/gin.H"
}
}
}
},
"delete": {
"security": [
{
"AdminAuth": []
}
],
"description": "Delete a global IP/CIDR ban rule",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"admin",
"ip-bans"
],
"summary": "Delete an IP ban",
"parameters": [
{
"type": "integer",
"description": "IP Ban ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"204": {
"description": "No Content"
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/gin.H"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/gin.H"
}
}
}
}
},
"/admin/keys/{id}/access": {
"get": {
"security": [
@@ -1617,7 +1889,7 @@ const docTemplate = `{
"AdminAuth": []
}
],
"description": "Get time × model aggregated data for stacked traffic charts. Returns time buckets with per-model breakdown.",
"description": "Get time × model aggregated data for stacked traffic charts. Returns a shared time axis under ` + "`" + `x` + "`" + ` and per-model series arrays aligned to that axis. Models outside top_n are aggregated under the series name \"other\".",
"produces": [
"application/json"
],
@@ -3522,7 +3794,7 @@ const docTemplate = `{
"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\", ...}",
"description": "Returns complete identity and realtime statistics of the authenticated user.\nSupports Admin Token, Master Key, and Child Key (API Key) authentication.\nThis endpoint is designed for frontend initialization - call once after login\nand store the response for subsequent use.\n\n**Response varies by token type:**\n\n**Admin Token:**\n- type: \"admin\"\n- role: \"admin\"\n- permissions: [\"*\"] (full access)\n\n**Master Key:**\n- type: \"master\"\n- Basic info: id, name, group, namespaces, status, epoch, max_child_keys, global_qps\n- Timestamps: created_at, updated_at\n- Realtime stats: requests, tokens, qps, qps_limit, rate_limited\n\n**Child Key (API Key):**\n- type: \"key\"\n- Basic info: id, master_id, master_name, group, scopes, namespaces, status\n- Security: issued_at_epoch, issued_by, allow_ips, deny_ips, expires_at\n- Model limits: model_limits, model_limits_enabled\n- Quota: quota_limit, quota_used, quota_reset_at, quota_reset_type\n- Usage stats: request_count, used_tokens, last_accessed_at\n- Realtime stats: requests, tokens, qps, qps_limit, rate_limited\n\n**Error responses:**\n- 401: authorization header required\n- 401: invalid authorization header format\n- 401: invalid token\n- 401: token is not active\n- 401: token has expired\n- 401: token has been revoked\n- 401: master is not active",
"produces": [
"application/json"
],
@@ -5157,6 +5429,23 @@ const docTemplate = `{
}
}
},
"internal_api.CreateIPBanRequest": {
"type": "object",
"required": [
"cidr"
],
"properties": {
"cidr": {
"type": "string"
},
"expires_at": {
"type": "integer"
},
"reason": {
"type": "string"
}
}
},
"internal_api.CreateMasterRequest": {
"type": "object",
"required": [
@@ -5208,11 +5497,36 @@ const docTemplate = `{
"$ref": "#/definitions/internal_api.TopModelStat"
}
},
"trends": {
"description": "Only present when include_trends=true",
"allOf": [
{
"$ref": "#/definitions/internal_api.DashboardTrends"
}
]
},
"updated_at": {
"type": "integer"
}
}
},
"internal_api.DashboardTrends": {
"type": "object",
"properties": {
"error_rate": {
"$ref": "#/definitions/internal_api.TrendInfo"
},
"latency": {
"$ref": "#/definitions/internal_api.TrendInfo"
},
"requests": {
"$ref": "#/definitions/internal_api.TrendInfo"
},
"tokens": {
"$ref": "#/definitions/internal_api.TrendInfo"
}
}
},
"internal_api.DeleteLogsRequest": {
"type": "object",
"properties": {
@@ -5283,6 +5597,38 @@ const docTemplate = `{
}
}
},
"internal_api.IPBanView": {
"type": "object",
"properties": {
"cidr": {
"type": "string"
},
"created_at": {
"type": "integer"
},
"created_by": {
"type": "string"
},
"expires_at": {
"type": "integer"
},
"hit_count": {
"type": "integer"
},
"id": {
"type": "integer"
},
"reason": {
"type": "string"
},
"status": {
"type": "string"
},
"updated_at": {
"type": "integer"
}
}
},
"internal_api.IssueChildKeyRequest": {
"type": "object",
"properties": {
@@ -5907,42 +6253,36 @@ const docTemplate = `{
}
}
},
"internal_api.TrafficBucket": {
"internal_api.TrafficChartAxis": {
"type": "object",
"properties": {
"breakdown": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/internal_api.TrafficMetrics"
"labels": {
"type": "array",
"items": {
"type": "string"
}
},
"time": {
"type": "string"
"timestamps": {
"type": "array",
"items": {
"type": "integer"
}
},
"timestamp": {
"type": "integer"
},
"total": {
"$ref": "#/definitions/internal_api.TrafficMetrics"
"totals": {
"$ref": "#/definitions/internal_api.TrafficTotals"
}
}
},
"internal_api.TrafficChartResponse": {
"type": "object",
"properties": {
"buckets": {
"type": "array",
"items": {
"$ref": "#/definitions/internal_api.TrafficBucket"
}
},
"granularity": {
"type": "string"
},
"models": {
"series": {
"type": "array",
"items": {
"type": "string"
"$ref": "#/definitions/internal_api.TrafficSeries"
}
},
"since": {
@@ -5950,20 +6290,71 @@ const docTemplate = `{
},
"until": {
"type": "integer"
},
"x": {
"$ref": "#/definitions/internal_api.TrafficChartAxis"
}
}
},
"internal_api.TrafficMetrics": {
"internal_api.TrafficSeries": {
"type": "object",
"properties": {
"count": {
"type": "integer"
"data": {
"type": "array",
"items": {
"type": "integer"
}
},
"name": {
"type": "string"
},
"tokens_in": {
"type": "integer"
"type": "array",
"items": {
"type": "integer"
}
},
"tokens_out": {
"type": "integer"
"type": "array",
"items": {
"type": "integer"
}
}
}
},
"internal_api.TrafficTotals": {
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"type": "integer"
}
},
"tokens_in": {
"type": "array",
"items": {
"type": "integer"
}
},
"tokens_out": {
"type": "array",
"items": {
"type": "integer"
}
}
}
},
"internal_api.TrendInfo": {
"type": "object",
"properties": {
"delta": {
"description": "Percentage change from previous period (nil if no baseline)",
"type": "number"
},
"direction": {
"description": "\"up\", \"down\", \"stable\", or \"new\" (no baseline)",
"type": "string"
}
}
},
@@ -6010,6 +6401,20 @@ const docTemplate = `{
}
}
},
"internal_api.UpdateIPBanRequest": {
"type": "object",
"properties": {
"expires_at": {
"$ref": "#/definitions/internal_api.optionalInt64"
},
"reason": {
"type": "string"
},
"status": {
"type": "string"
}
}
},
"internal_api.UpdateMasterRequest": {
"type": "object",
"properties": {
@@ -6071,9 +6476,49 @@ const docTemplate = `{
}
}
},
"internal_api.WhoamiRealtimeView": {
"type": "object",
"properties": {
"qps": {
"description": "Current QPS",
"type": "integer",
"example": 5
},
"qps_limit": {
"description": "QPS limit",
"type": "integer",
"example": 100
},
"rate_limited": {
"description": "Whether currently rate limited",
"type": "boolean",
"example": false
},
"requests": {
"description": "Total requests",
"type": "integer",
"example": 100
},
"tokens": {
"description": "Total tokens used",
"type": "integer",
"example": 50000
},
"updated_at": {
"description": "Last updated timestamp",
"type": "integer",
"example": 1703505600
}
}
},
"internal_api.WhoamiResponse": {
"type": "object",
"properties": {
"allow_ips": {
"description": "IP whitelist (for diagnostics)",
"type": "string",
"example": ""
},
"created_at": {
"type": "integer",
"example": 1703505600
@@ -6082,10 +6527,20 @@ const docTemplate = `{
"type": "string",
"example": "default"
},
"deny_ips": {
"description": "IP blacklist (for diagnostics)",
"type": "string",
"example": ""
},
"epoch": {
"type": "integer",
"example": 1
},
"expires_at": {
"description": "Expiration timestamp (0 = never)",
"type": "integer",
"example": 0
},
"global_qps": {
"type": "integer",
"example": 100
@@ -6107,15 +6562,35 @@ const docTemplate = `{
"type": "string",
"example": "master"
},
"last_accessed_at": {
"description": "Last access timestamp",
"type": "integer",
"example": 0
},
"master_id": {
"description": "Key fields (only present when type is \"key\")",
"type": "integer",
"example": 1
},
"master_name": {
"description": "Parent master name (for display)",
"type": "string",
"example": "tenant-a"
},
"max_child_keys": {
"type": "integer",
"example": 5
},
"model_limits": {
"description": "Model restrictions",
"type": "string",
"example": "gpt-4,claude"
},
"model_limits_enabled": {
"description": "Whether model limits are active",
"type": "boolean",
"example": false
},
"name": {
"type": "string",
"example": "tenant-a"
@@ -6124,6 +6599,46 @@ const docTemplate = `{
"type": "string",
"example": "default,ns1"
},
"permissions": {
"description": "Admin permissions (always [\"*\"])",
"type": "array",
"items": {
"type": "string"
}
},
"quota_limit": {
"description": "Token quota limit (-1 = unlimited)",
"type": "integer",
"example": -1
},
"quota_reset_at": {
"description": "Quota reset timestamp",
"type": "integer",
"example": 0
},
"quota_reset_type": {
"description": "Quota reset type",
"type": "string",
"example": "monthly"
},
"quota_used": {
"description": "Token quota used",
"type": "integer",
"example": 0
},
"realtime": {
"description": "Realtime stats (for master and key types)",
"allOf": [
{
"$ref": "#/definitions/internal_api.WhoamiRealtimeView"
}
]
},
"request_count": {
"description": "Total request count (from DB)",
"type": "integer",
"example": 0
},
"role": {
"description": "Admin fields (only present when type is \"admin\")",
"type": "string",
@@ -6145,6 +6660,11 @@ const docTemplate = `{
"updated_at": {
"type": "integer",
"example": 1703505600
},
"used_tokens": {
"description": "Total tokens used (from DB)",
"type": "integer",
"example": 0
}
}
},
@@ -6173,6 +6693,18 @@ const docTemplate = `{
}
}
},
"internal_api.optionalInt64": {
"type": "object",
"properties": {
"set": {
"type": "boolean"
},
"value": {
"type": "integer",
"format": "int64"
}
}
},
"internal_api.refreshModelRegistryRequest": {
"type": "object",
"properties": {

View File

@@ -1177,7 +1177,7 @@
"parameters": [
{
"type": "string",
"description": "time period: today, week, month, all",
"description": "time period: today, week, month, last7d, last30d, all",
"name": "period",
"in": "query"
},
@@ -1192,6 +1192,12 @@
"description": "unix seconds",
"name": "until",
"in": "query"
},
{
"type": "boolean",
"description": "include trend data comparing to previous period",
"name": "include_trends",
"in": "query"
}
],
"responses": {
@@ -1296,6 +1302,272 @@
}
}
},
"/admin/ip-bans": {
"get": {
"security": [
{
"AdminAuth": []
}
],
"description": "List all global IP/CIDR ban rules",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"admin",
"ip-bans"
],
"summary": "List IP bans",
"parameters": [
{
"type": "string",
"description": "Filter by status (active, expired)",
"name": "status",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/internal_api.IPBanView"
}
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/gin.H"
}
}
}
},
"post": {
"security": [
{
"AdminAuth": []
}
],
"description": "Create a new global IP/CIDR ban rule",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"admin",
"ip-bans"
],
"summary": "Create an IP ban",
"parameters": [
{
"description": "IP Ban Info",
"name": "ban",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/internal_api.CreateIPBanRequest"
}
}
],
"responses": {
"201": {
"description": "Created",
"schema": {
"$ref": "#/definitions/internal_api.IPBanView"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/gin.H"
}
},
"409": {
"description": "Conflict",
"schema": {
"$ref": "#/definitions/gin.H"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/gin.H"
}
}
}
}
},
"/admin/ip-bans/{id}": {
"get": {
"security": [
{
"AdminAuth": []
}
],
"description": "Get a single global IP/CIDR ban rule by ID",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"admin",
"ip-bans"
],
"summary": "Get an IP ban",
"parameters": [
{
"type": "integer",
"description": "IP Ban ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/internal_api.IPBanView"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/gin.H"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/gin.H"
}
}
}
},
"put": {
"security": [
{
"AdminAuth": []
}
],
"description": "Update a global IP/CIDR ban rule",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"admin",
"ip-bans"
],
"summary": "Update an IP ban",
"parameters": [
{
"type": "integer",
"description": "IP Ban ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "IP Ban Update",
"name": "ban",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/internal_api.UpdateIPBanRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/internal_api.IPBanView"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/gin.H"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/gin.H"
}
},
"409": {
"description": "Conflict",
"schema": {
"$ref": "#/definitions/gin.H"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/gin.H"
}
}
}
},
"delete": {
"security": [
{
"AdminAuth": []
}
],
"description": "Delete a global IP/CIDR ban rule",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"admin",
"ip-bans"
],
"summary": "Delete an IP ban",
"parameters": [
{
"type": "integer",
"description": "IP Ban ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"204": {
"description": "No Content"
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/gin.H"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/gin.H"
}
}
}
}
},
"/admin/keys/{id}/access": {
"get": {
"security": [
@@ -1611,7 +1883,7 @@
"AdminAuth": []
}
],
"description": "Get time × model aggregated data for stacked traffic charts. Returns time buckets with per-model breakdown.",
"description": "Get time × model aggregated data for stacked traffic charts. Returns a shared time axis under `x` and per-model series arrays aligned to that axis. Models outside top_n are aggregated under the series name \"other\".",
"produces": [
"application/json"
],
@@ -3516,7 +3788,7 @@
"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\", ...}",
"description": "Returns complete identity and realtime statistics of the authenticated user.\nSupports Admin Token, Master Key, and Child Key (API Key) authentication.\nThis endpoint is designed for frontend initialization - call once after login\nand store the response for subsequent use.\n\n**Response varies by token type:**\n\n**Admin Token:**\n- type: \"admin\"\n- role: \"admin\"\n- permissions: [\"*\"] (full access)\n\n**Master Key:**\n- type: \"master\"\n- Basic info: id, name, group, namespaces, status, epoch, max_child_keys, global_qps\n- Timestamps: created_at, updated_at\n- Realtime stats: requests, tokens, qps, qps_limit, rate_limited\n\n**Child Key (API Key):**\n- type: \"key\"\n- Basic info: id, master_id, master_name, group, scopes, namespaces, status\n- Security: issued_at_epoch, issued_by, allow_ips, deny_ips, expires_at\n- Model limits: model_limits, model_limits_enabled\n- Quota: quota_limit, quota_used, quota_reset_at, quota_reset_type\n- Usage stats: request_count, used_tokens, last_accessed_at\n- Realtime stats: requests, tokens, qps, qps_limit, rate_limited\n\n**Error responses:**\n- 401: authorization header required\n- 401: invalid authorization header format\n- 401: invalid token\n- 401: token is not active\n- 401: token has expired\n- 401: token has been revoked\n- 401: master is not active",
"produces": [
"application/json"
],
@@ -5151,6 +5423,23 @@
}
}
},
"internal_api.CreateIPBanRequest": {
"type": "object",
"required": [
"cidr"
],
"properties": {
"cidr": {
"type": "string"
},
"expires_at": {
"type": "integer"
},
"reason": {
"type": "string"
}
}
},
"internal_api.CreateMasterRequest": {
"type": "object",
"required": [
@@ -5202,11 +5491,36 @@
"$ref": "#/definitions/internal_api.TopModelStat"
}
},
"trends": {
"description": "Only present when include_trends=true",
"allOf": [
{
"$ref": "#/definitions/internal_api.DashboardTrends"
}
]
},
"updated_at": {
"type": "integer"
}
}
},
"internal_api.DashboardTrends": {
"type": "object",
"properties": {
"error_rate": {
"$ref": "#/definitions/internal_api.TrendInfo"
},
"latency": {
"$ref": "#/definitions/internal_api.TrendInfo"
},
"requests": {
"$ref": "#/definitions/internal_api.TrendInfo"
},
"tokens": {
"$ref": "#/definitions/internal_api.TrendInfo"
}
}
},
"internal_api.DeleteLogsRequest": {
"type": "object",
"properties": {
@@ -5277,6 +5591,38 @@
}
}
},
"internal_api.IPBanView": {
"type": "object",
"properties": {
"cidr": {
"type": "string"
},
"created_at": {
"type": "integer"
},
"created_by": {
"type": "string"
},
"expires_at": {
"type": "integer"
},
"hit_count": {
"type": "integer"
},
"id": {
"type": "integer"
},
"reason": {
"type": "string"
},
"status": {
"type": "string"
},
"updated_at": {
"type": "integer"
}
}
},
"internal_api.IssueChildKeyRequest": {
"type": "object",
"properties": {
@@ -5901,42 +6247,36 @@
}
}
},
"internal_api.TrafficBucket": {
"internal_api.TrafficChartAxis": {
"type": "object",
"properties": {
"breakdown": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/internal_api.TrafficMetrics"
"labels": {
"type": "array",
"items": {
"type": "string"
}
},
"time": {
"type": "string"
"timestamps": {
"type": "array",
"items": {
"type": "integer"
}
},
"timestamp": {
"type": "integer"
},
"total": {
"$ref": "#/definitions/internal_api.TrafficMetrics"
"totals": {
"$ref": "#/definitions/internal_api.TrafficTotals"
}
}
},
"internal_api.TrafficChartResponse": {
"type": "object",
"properties": {
"buckets": {
"type": "array",
"items": {
"$ref": "#/definitions/internal_api.TrafficBucket"
}
},
"granularity": {
"type": "string"
},
"models": {
"series": {
"type": "array",
"items": {
"type": "string"
"$ref": "#/definitions/internal_api.TrafficSeries"
}
},
"since": {
@@ -5944,20 +6284,71 @@
},
"until": {
"type": "integer"
},
"x": {
"$ref": "#/definitions/internal_api.TrafficChartAxis"
}
}
},
"internal_api.TrafficMetrics": {
"internal_api.TrafficSeries": {
"type": "object",
"properties": {
"count": {
"type": "integer"
"data": {
"type": "array",
"items": {
"type": "integer"
}
},
"name": {
"type": "string"
},
"tokens_in": {
"type": "integer"
"type": "array",
"items": {
"type": "integer"
}
},
"tokens_out": {
"type": "integer"
"type": "array",
"items": {
"type": "integer"
}
}
}
},
"internal_api.TrafficTotals": {
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"type": "integer"
}
},
"tokens_in": {
"type": "array",
"items": {
"type": "integer"
}
},
"tokens_out": {
"type": "array",
"items": {
"type": "integer"
}
}
}
},
"internal_api.TrendInfo": {
"type": "object",
"properties": {
"delta": {
"description": "Percentage change from previous period (nil if no baseline)",
"type": "number"
},
"direction": {
"description": "\"up\", \"down\", \"stable\", or \"new\" (no baseline)",
"type": "string"
}
}
},
@@ -6004,6 +6395,20 @@
}
}
},
"internal_api.UpdateIPBanRequest": {
"type": "object",
"properties": {
"expires_at": {
"$ref": "#/definitions/internal_api.optionalInt64"
},
"reason": {
"type": "string"
},
"status": {
"type": "string"
}
}
},
"internal_api.UpdateMasterRequest": {
"type": "object",
"properties": {
@@ -6065,9 +6470,49 @@
}
}
},
"internal_api.WhoamiRealtimeView": {
"type": "object",
"properties": {
"qps": {
"description": "Current QPS",
"type": "integer",
"example": 5
},
"qps_limit": {
"description": "QPS limit",
"type": "integer",
"example": 100
},
"rate_limited": {
"description": "Whether currently rate limited",
"type": "boolean",
"example": false
},
"requests": {
"description": "Total requests",
"type": "integer",
"example": 100
},
"tokens": {
"description": "Total tokens used",
"type": "integer",
"example": 50000
},
"updated_at": {
"description": "Last updated timestamp",
"type": "integer",
"example": 1703505600
}
}
},
"internal_api.WhoamiResponse": {
"type": "object",
"properties": {
"allow_ips": {
"description": "IP whitelist (for diagnostics)",
"type": "string",
"example": ""
},
"created_at": {
"type": "integer",
"example": 1703505600
@@ -6076,10 +6521,20 @@
"type": "string",
"example": "default"
},
"deny_ips": {
"description": "IP blacklist (for diagnostics)",
"type": "string",
"example": ""
},
"epoch": {
"type": "integer",
"example": 1
},
"expires_at": {
"description": "Expiration timestamp (0 = never)",
"type": "integer",
"example": 0
},
"global_qps": {
"type": "integer",
"example": 100
@@ -6101,15 +6556,35 @@
"type": "string",
"example": "master"
},
"last_accessed_at": {
"description": "Last access timestamp",
"type": "integer",
"example": 0
},
"master_id": {
"description": "Key fields (only present when type is \"key\")",
"type": "integer",
"example": 1
},
"master_name": {
"description": "Parent master name (for display)",
"type": "string",
"example": "tenant-a"
},
"max_child_keys": {
"type": "integer",
"example": 5
},
"model_limits": {
"description": "Model restrictions",
"type": "string",
"example": "gpt-4,claude"
},
"model_limits_enabled": {
"description": "Whether model limits are active",
"type": "boolean",
"example": false
},
"name": {
"type": "string",
"example": "tenant-a"
@@ -6118,6 +6593,46 @@
"type": "string",
"example": "default,ns1"
},
"permissions": {
"description": "Admin permissions (always [\"*\"])",
"type": "array",
"items": {
"type": "string"
}
},
"quota_limit": {
"description": "Token quota limit (-1 = unlimited)",
"type": "integer",
"example": -1
},
"quota_reset_at": {
"description": "Quota reset timestamp",
"type": "integer",
"example": 0
},
"quota_reset_type": {
"description": "Quota reset type",
"type": "string",
"example": "monthly"
},
"quota_used": {
"description": "Token quota used",
"type": "integer",
"example": 0
},
"realtime": {
"description": "Realtime stats (for master and key types)",
"allOf": [
{
"$ref": "#/definitions/internal_api.WhoamiRealtimeView"
}
]
},
"request_count": {
"description": "Total request count (from DB)",
"type": "integer",
"example": 0
},
"role": {
"description": "Admin fields (only present when type is \"admin\")",
"type": "string",
@@ -6139,6 +6654,11 @@
"updated_at": {
"type": "integer",
"example": 1703505600
},
"used_tokens": {
"description": "Total tokens used (from DB)",
"type": "integer",
"example": 0
}
}
},
@@ -6167,6 +6687,18 @@
}
}
},
"internal_api.optionalInt64": {
"type": "object",
"properties": {
"set": {
"type": "boolean"
},
"value": {
"type": "integer",
"format": "int64"
}
}
},
"internal_api.refreshModelRegistryRequest": {
"type": "object",
"properties": {

View File

@@ -562,6 +562,17 @@ definitions:
- title
- type
type: object
internal_api.CreateIPBanRequest:
properties:
cidr:
type: string
expires_at:
type: integer
reason:
type: string
required:
- cidr
type: object
internal_api.CreateMasterRequest:
properties:
global_qps:
@@ -596,9 +607,24 @@ definitions:
items:
$ref: '#/definitions/internal_api.TopModelStat'
type: array
trends:
allOf:
- $ref: '#/definitions/internal_api.DashboardTrends'
description: Only present when include_trends=true
updated_at:
type: integer
type: object
internal_api.DashboardTrends:
properties:
error_rate:
$ref: '#/definitions/internal_api.TrendInfo'
latency:
$ref: '#/definitions/internal_api.TrendInfo'
requests:
$ref: '#/definitions/internal_api.TrendInfo'
tokens:
$ref: '#/definitions/internal_api.TrendInfo'
type: object
internal_api.DeleteLogsRequest:
properties:
before:
@@ -646,6 +672,27 @@ definitions:
$ref: '#/definitions/internal_api.GroupedStatsItem'
type: array
type: object
internal_api.IPBanView:
properties:
cidr:
type: string
created_at:
type: integer
created_by:
type: string
expires_at:
type: integer
hit_count:
type: integer
id:
type: integer
reason:
type: string
status:
type: string
updated_at:
type: integer
type: object
internal_api.IssueChildKeyRequest:
properties:
allow_ips:
@@ -1055,44 +1102,74 @@ definitions:
tokens:
type: integer
type: object
internal_api.TrafficBucket:
internal_api.TrafficChartAxis:
properties:
breakdown:
additionalProperties:
$ref: '#/definitions/internal_api.TrafficMetrics'
type: object
time:
type: string
timestamp:
type: integer
total:
$ref: '#/definitions/internal_api.TrafficMetrics'
labels:
items:
type: string
type: array
timestamps:
items:
type: integer
type: array
totals:
$ref: '#/definitions/internal_api.TrafficTotals'
type: object
internal_api.TrafficChartResponse:
properties:
buckets:
items:
$ref: '#/definitions/internal_api.TrafficBucket'
type: array
granularity:
type: string
models:
series:
items:
type: string
$ref: '#/definitions/internal_api.TrafficSeries'
type: array
since:
type: integer
until:
type: integer
x:
$ref: '#/definitions/internal_api.TrafficChartAxis'
type: object
internal_api.TrafficMetrics:
internal_api.TrafficSeries:
properties:
count:
type: integer
data:
items:
type: integer
type: array
name:
type: string
tokens_in:
type: integer
items:
type: integer
type: array
tokens_out:
type: integer
items:
type: integer
type: array
type: object
internal_api.TrafficTotals:
properties:
data:
items:
type: integer
type: array
tokens_in:
items:
type: integer
type: array
tokens_out:
items:
type: integer
type: array
type: object
internal_api.TrendInfo:
properties:
delta:
description: Percentage change from previous period (nil if no baseline)
type: number
direction:
description: '"up", "down", "stable", or "new" (no baseline)'
type: string
type: object
internal_api.UpdateAccessRequest:
properties:
@@ -1122,6 +1199,15 @@ definitions:
min_tpm_tokens_1m:
type: integer
type: object
internal_api.UpdateIPBanRequest:
properties:
expires_at:
$ref: '#/definitions/internal_api.optionalInt64'
reason:
type: string
status:
type: string
type: object
internal_api.UpdateMasterRequest:
properties:
global_qps:
@@ -1162,17 +1248,56 @@ definitions:
description: active/suspended
type: string
type: object
internal_api.WhoamiRealtimeView:
properties:
qps:
description: Current QPS
example: 5
type: integer
qps_limit:
description: QPS limit
example: 100
type: integer
rate_limited:
description: Whether currently rate limited
example: false
type: boolean
requests:
description: Total requests
example: 100
type: integer
tokens:
description: Total tokens used
example: 50000
type: integer
updated_at:
description: Last updated timestamp
example: 1703505600
type: integer
type: object
internal_api.WhoamiResponse:
properties:
allow_ips:
description: IP whitelist (for diagnostics)
example: ""
type: string
created_at:
example: 1703505600
type: integer
default_namespace:
example: default
type: string
deny_ips:
description: IP blacklist (for diagnostics)
example: ""
type: string
epoch:
example: 1
type: integer
expires_at:
description: Expiration timestamp (0 = never)
example: 0
type: integer
global_qps:
example: 100
type: integer
@@ -1189,19 +1314,64 @@ definitions:
issued_by:
example: master
type: string
last_accessed_at:
description: Last access timestamp
example: 0
type: integer
master_id:
description: Key fields (only present when type is "key")
example: 1
type: integer
master_name:
description: Parent master name (for display)
example: tenant-a
type: string
max_child_keys:
example: 5
type: integer
model_limits:
description: Model restrictions
example: gpt-4,claude
type: string
model_limits_enabled:
description: Whether model limits are active
example: false
type: boolean
name:
example: tenant-a
type: string
namespaces:
example: default,ns1
type: string
permissions:
description: Admin permissions (always ["*"])
items:
type: string
type: array
quota_limit:
description: Token quota limit (-1 = unlimited)
example: -1
type: integer
quota_reset_at:
description: Quota reset timestamp
example: 0
type: integer
quota_reset_type:
description: Quota reset type
example: monthly
type: string
quota_used:
description: Token quota used
example: 0
type: integer
realtime:
allOf:
- $ref: '#/definitions/internal_api.WhoamiRealtimeView'
description: Realtime stats (for master and key types)
request_count:
description: Total request count (from DB)
example: 0
type: integer
role:
description: Admin fields (only present when type is "admin")
example: admin
@@ -1219,6 +1389,10 @@ definitions:
updated_at:
example: 1703505600
type: integer
used_tokens:
description: Total tokens used (from DB)
example: 0
type: integer
type: object
internal_api.apiKeyStatsFlushEntry:
properties:
@@ -1236,6 +1410,14 @@ definitions:
$ref: '#/definitions/internal_api.apiKeyStatsFlushEntry'
type: array
type: object
internal_api.optionalInt64:
properties:
set:
type: boolean
value:
format: int64
type: integer
type: object
internal_api.refreshModelRegistryRequest:
properties:
ref:
@@ -2086,7 +2268,7 @@ paths:
description: Returns aggregated metrics for dashboard display including requests,
tokens, latency, masters, keys, and provider keys statistics
parameters:
- description: 'time period: today, week, month, all'
- description: 'time period: today, week, month, last7d, last30d, all'
in: query
name: period
type: string
@@ -2098,6 +2280,10 @@ paths:
in: query
name: until
type: integer
- description: include trend data comparing to previous period
in: query
name: include_trends
type: boolean
produces:
- application/json
responses:
@@ -2169,6 +2355,177 @@ paths:
summary: Update feature flags
tags:
- admin
/admin/ip-bans:
get:
consumes:
- application/json
description: List all global IP/CIDR ban rules
parameters:
- description: Filter by status (active, expired)
in: query
name: status
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
items:
$ref: '#/definitions/internal_api.IPBanView'
type: array
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/gin.H'
security:
- AdminAuth: []
summary: List IP bans
tags:
- admin
- ip-bans
post:
consumes:
- application/json
description: Create a new global IP/CIDR ban rule
parameters:
- description: IP Ban Info
in: body
name: ban
required: true
schema:
$ref: '#/definitions/internal_api.CreateIPBanRequest'
produces:
- application/json
responses:
"201":
description: Created
schema:
$ref: '#/definitions/internal_api.IPBanView'
"400":
description: Bad Request
schema:
$ref: '#/definitions/gin.H'
"409":
description: Conflict
schema:
$ref: '#/definitions/gin.H'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/gin.H'
security:
- AdminAuth: []
summary: Create an IP ban
tags:
- admin
- ip-bans
/admin/ip-bans/{id}:
delete:
consumes:
- application/json
description: Delete a global IP/CIDR ban rule
parameters:
- description: IP Ban ID
in: path
name: id
required: true
type: integer
produces:
- application/json
responses:
"204":
description: No Content
"404":
description: Not Found
schema:
$ref: '#/definitions/gin.H'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/gin.H'
security:
- AdminAuth: []
summary: Delete an IP ban
tags:
- admin
- ip-bans
get:
consumes:
- application/json
description: Get a single global IP/CIDR ban rule by ID
parameters:
- description: IP Ban ID
in: path
name: id
required: true
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/internal_api.IPBanView'
"404":
description: Not Found
schema:
$ref: '#/definitions/gin.H'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/gin.H'
security:
- AdminAuth: []
summary: Get an IP ban
tags:
- admin
- ip-bans
put:
consumes:
- application/json
description: Update a global IP/CIDR ban rule
parameters:
- description: IP Ban ID
in: path
name: id
required: true
type: integer
- description: IP Ban Update
in: body
name: ban
required: true
schema:
$ref: '#/definitions/internal_api.UpdateIPBanRequest'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/internal_api.IPBanView'
"400":
description: Bad Request
schema:
$ref: '#/definitions/gin.H'
"404":
description: Not Found
schema:
$ref: '#/definitions/gin.H'
"409":
description: Conflict
schema:
$ref: '#/definitions/gin.H'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/gin.H'
security:
- AdminAuth: []
summary: Update an IP ban
tags:
- admin
- ip-bans
/admin/keys/{id}/access:
get:
description: Returns key default_namespace and namespaces
@@ -2375,7 +2732,8 @@ paths:
/admin/logs/stats/traffic-chart:
get:
description: Get time × model aggregated data for stacked traffic charts. Returns
time buckets with per-model breakdown.
a shared time axis under `x` and per-model series arrays aligned to that axis.
Models outside top_n are aggregated under the series name "other".
parameters:
- description: 'Time granularity: hour (default) or minute'
enum:
@@ -3587,13 +3945,41 @@ paths:
/auth/whoami:
get:
description: |-
Returns the identity of the authenticated user based on the Authorization header.
Returns complete identity and realtime statistics of the authenticated user.
Supports Admin Token, Master Key, and Child Key (API Key) authentication.
This endpoint is designed for frontend initialization - call once after login
and store the response for subsequent use.
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", ...}
**Response varies by token type:**
**Admin Token:**
- type: "admin"
- role: "admin"
- permissions: ["*"] (full access)
**Master Key:**
- type: "master"
- Basic info: id, name, group, namespaces, status, epoch, max_child_keys, global_qps
- Timestamps: created_at, updated_at
- Realtime stats: requests, tokens, qps, qps_limit, rate_limited
**Child Key (API Key):**
- type: "key"
- Basic info: id, master_id, master_name, group, scopes, namespaces, status
- Security: issued_at_epoch, issued_by, allow_ips, deny_ips, expires_at
- Model limits: model_limits, model_limits_enabled
- Quota: quota_limit, quota_used, quota_reset_at, quota_reset_type
- Usage stats: request_count, used_tokens, last_accessed_at
- Realtime stats: requests, tokens, qps, qps_limit, rate_limited
**Error responses:**
- 401: authorization header required
- 401: invalid authorization header format
- 401: invalid token
- 401: token is not active
- 401: token has expired
- 401: token has been revoked
- 401: master is not active
produces:
- application/json
responses: