From c421be3ae9e624c4d23ff49a13f4c8b8d674acdb Mon Sep 17 00:00:00 2001 From: mac Date: Thu, 23 Jul 2026 12:05:21 +0800 Subject: [PATCH] docs(delivery): add Swagger annotations and regenerate API docs Add Swagger annotations to all 7 delivery handlers. Fix existing test.go type references (Response -> response.Response). Add swaggo dependencies to go.mod. Regenerate swagger.json/yaml/docs.go with delivery endpoints visible in /swagger/index.html. Relates-to: #97 --- server/docs/docs.go | 544 ++++++++++++++++++++++++++++++-- server/docs/swagger.json | 544 ++++++++++++++++++++++++++++++-- server/docs/swagger.yaml | 370 ++++++++++++++++++++-- server/go.mod | 5 + server/go.sum | 12 + server/internal/handler/test.go | 20 +- 6 files changed, 1429 insertions(+), 66 deletions(-) diff --git a/server/docs/docs.go b/server/docs/docs.go index 9a6275c..763ede7 100644 --- a/server/docs/docs.go +++ b/server/docs/docs.go @@ -24,6 +24,357 @@ const docTemplate = `{ "host": "{{.Host}}", "basePath": "{{.BasePath}}", "paths": { + "/auth/api/v1/delivery/mysql": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "创建一个 MySQL 交付任务,调度器会自动分配主机、调用 AWX 执行部署", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "delivery" + ], + "summary": "提交 MySQL 一键交付", + "parameters": [ + { + "type": "string", + "description": "幂等键(防重复提交,最长 128 字符)", + "name": "Idempotency-Key", + "in": "header", + "required": true + }, + { + "description": "交付参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/service.MySQLDeliveryInput" + } + } + ], + "responses": { + "200": { + "description": "幂等重放(相同 Idempotency-Key 已存在)", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "202": { + "description": "任务已创建", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "400": { + "description": "参数错误", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "401": { + "description": "未授权", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/auth/api/v1/delivery/quotas": { + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "管理员为指定业务线 + 部署目标设置资源配额", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "delivery" + ], + "summary": "创建或更新资源配额", + "parameters": [ + { + "description": "配额参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/handler.quotaPayload" + } + } + ], + "responses": { + "200": { + "description": "配额已更新", + "schema": { + "$ref": "#/definitions/model.ResourceQuota" + } + }, + "400": { + "description": "参数错误", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "401": { + "description": "未授权", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/auth/api/v1/delivery/targets": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "返回所有已启用的部署目标(如 k8s 集群、主机池)", + "produces": [ + "application/json" + ], + "tags": [ + "delivery" + ], + "summary": "获取可用部署目标", + "responses": { + "200": { + "description": "items: 部署目标数组", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "500": { + "description": "内部错误", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "管理员创建新的部署目标(目前仅支持 k8s 类型)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "delivery" + ], + "summary": "创建部署目标", + "parameters": [ + { + "description": "目标配置", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/handler.targetPayload" + } + } + ], + "responses": { + "201": { + "description": "目标已创建", + "schema": { + "$ref": "#/definitions/model.DeploymentTarget" + } + }, + "400": { + "description": "参数错误", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "401": { + "description": "未授权", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "409": { + "description": "名称冲突", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/auth/api/v1/delivery/tasks": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "返回当前用户可见的交付任务列表(管理员可见全部)", + "produces": [ + "application/json" + ], + "tags": [ + "delivery" + ], + "summary": "获取交付任务列表", + "parameters": [ + { + "type": "integer", + "description": "业务线 ID 过滤", + "name": "business_line_id", + "in": "query" + } + ], + "responses": { + "200": { + "description": "items: 任务数组", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "401": { + "description": "未授权", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/auth/api/v1/delivery/tasks/{id}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "返回指定任务的详细信息及事件流", + "produces": [ + "application/json" + ], + "tags": [ + "delivery" + ], + "summary": "获取交付任务详情", + "parameters": [ + { + "type": "string", + "description": "任务 ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "task + events", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "401": { + "description": "未授权", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "404": { + "description": "任务不存在", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/auth/api/v1/delivery/tasks/{id}/cancel": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "取消一个正在执行或等待中的交付任务", + "produces": [ + "application/json" + ], + "tags": [ + "delivery" + ], + "summary": "取消交付任务", + "parameters": [ + { + "type": "string", + "description": "任务 ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "ok: true", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "401": { + "description": "未授权", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "409": { + "description": "无法取消(状态冲突)", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, "/ping": { "get": { "description": "检查服务器是否正常运行", @@ -43,7 +394,7 @@ const docTemplate = `{ "schema": { "allOf": [ { - "$ref": "#/definitions/handler.Response" + "$ref": "#/definitions/response.Response" }, { "type": "object", @@ -81,7 +432,7 @@ const docTemplate = `{ "401": { "description": "Unauthorized", "schema": { - "$ref": "#/definitions/handler.Response" + "$ref": "#/definitions/response.Response" } } } @@ -104,7 +455,7 @@ const docTemplate = `{ "403": { "description": "Forbidden", "schema": { - "$ref": "#/definitions/handler.Response" + "$ref": "#/definitions/response.Response" } } } @@ -127,7 +478,7 @@ const docTemplate = `{ "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/handler.Response" + "$ref": "#/definitions/response.Response" } } } @@ -161,7 +512,7 @@ const docTemplate = `{ "schema": { "allOf": [ { - "$ref": "#/definitions/handler.Response" + "$ref": "#/definitions/response.Response" }, { "type": "object", @@ -177,7 +528,7 @@ const docTemplate = `{ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/handler.Response" + "$ref": "#/definitions/response.Response" } } } @@ -202,7 +553,7 @@ const docTemplate = `{ "schema": { "allOf": [ { - "$ref": "#/definitions/handler.Response" + "$ref": "#/definitions/response.Response" }, { "type": "object", @@ -210,7 +561,7 @@ const docTemplate = `{ "data": { "allOf": [ { - "$ref": "#/definitions/handler.PaginatedData" + "$ref": "#/definitions/response.PaginatedData" }, { "type": "object", @@ -252,7 +603,7 @@ const docTemplate = `{ "schema": { "allOf": [ { - "$ref": "#/definitions/handler.Response" + "$ref": "#/definitions/response.Response" }, { "type": "object", @@ -293,13 +644,13 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/handler.Response" + "$ref": "#/definitions/response.Response" } }, "408": { "description": "Request Timeout", "schema": { - "$ref": "#/definitions/handler.Response" + "$ref": "#/definitions/response.Response" } } } @@ -307,7 +658,129 @@ const docTemplate = `{ } }, "definitions": { - "handler.PaginatedData": { + "handler.quotaPayload": { + "type": "object", + "required": [ + "business_line_id", + "cpu_milli", + "instance_limit", + "memory_mi", + "storage_gi", + "target_id" + ], + "properties": { + "business_line_id": { + "type": "integer" + }, + "cpu_milli": { + "type": "integer" + }, + "instance_limit": { + "type": "integer" + }, + "memory_mi": { + "type": "integer" + }, + "storage_gi": { + "type": "integer" + }, + "target_id": { + "type": "integer" + } + } + }, + "handler.targetPayload": { + "type": "object", + "required": [ + "awx_inventory_id", + "awx_template_id", + "name", + "target_type" + ], + "properties": { + "awx_inventory_id": { + "type": "integer" + }, + "awx_template_id": { + "type": "integer" + }, + "metadata": { + "type": "object", + "additionalProperties": {} + }, + "name": { + "type": "string" + }, + "target_type": { + "type": "string" + } + } + }, + "model.DeploymentTarget": { + "type": "object", + "properties": { + "awx_inventory_id": { + "type": "integer" + }, + "awx_template_id": { + "type": "integer" + }, + "created_at": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "id": { + "type": "integer" + }, + "metadata": { + "type": "string" + }, + "name": { + "type": "string" + }, + "target_type": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + } + }, + "model.ResourceQuota": { + "type": "object", + "properties": { + "business_line_id": { + "type": "integer" + }, + "cpu_milli": { + "type": "integer" + }, + "created_at": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "instance_limit": { + "type": "integer" + }, + "memory_mi": { + "type": "integer" + }, + "storage_gi": { + "type": "integer" + }, + "target_id": { + "type": "integer" + }, + "updated_at": { + "type": "string" + } + } + }, + "response.PaginatedData": { "type": "object", "properties": { "items": {}, @@ -316,25 +789,58 @@ const docTemplate = `{ } } }, - "handler.Response": { + "response.Response": { "type": "object", "properties": { "code": { - "description": "业务错误码,0 表示成功", "type": "integer" }, - "data": { - "description": "响应数据,成功时返回" - }, + "data": {}, "details": { - "description": "错误详情,失败时返回", "type": "string" }, "message": { - "description": "响应消息", "type": "string" } } + }, + "service.MySQLDeliveryInput": { + "type": "object", + "required": [ + "business_line_id", + "cpu_milli", + "instance_name", + "memory_mi", + "namespace", + "storage_gi", + "target_id" + ], + "properties": { + "business_line_id": { + "type": "integer" + }, + "cpu_milli": { + "type": "integer" + }, + "instance_name": { + "type": "string" + }, + "memory_mi": { + "type": "integer" + }, + "mysql_version": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "storage_gi": { + "type": "integer" + }, + "target_id": { + "type": "integer" + } + } } }, "securityDefinitions": { diff --git a/server/docs/swagger.json b/server/docs/swagger.json index 17a1625..a794ca0 100644 --- a/server/docs/swagger.json +++ b/server/docs/swagger.json @@ -17,6 +17,357 @@ }, "basePath": "/api/v1", "paths": { + "/auth/api/v1/delivery/mysql": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "创建一个 MySQL 交付任务,调度器会自动分配主机、调用 AWX 执行部署", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "delivery" + ], + "summary": "提交 MySQL 一键交付", + "parameters": [ + { + "type": "string", + "description": "幂等键(防重复提交,最长 128 字符)", + "name": "Idempotency-Key", + "in": "header", + "required": true + }, + { + "description": "交付参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/service.MySQLDeliveryInput" + } + } + ], + "responses": { + "200": { + "description": "幂等重放(相同 Idempotency-Key 已存在)", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "202": { + "description": "任务已创建", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "400": { + "description": "参数错误", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "401": { + "description": "未授权", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/auth/api/v1/delivery/quotas": { + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "管理员为指定业务线 + 部署目标设置资源配额", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "delivery" + ], + "summary": "创建或更新资源配额", + "parameters": [ + { + "description": "配额参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/handler.quotaPayload" + } + } + ], + "responses": { + "200": { + "description": "配额已更新", + "schema": { + "$ref": "#/definitions/model.ResourceQuota" + } + }, + "400": { + "description": "参数错误", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "401": { + "description": "未授权", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/auth/api/v1/delivery/targets": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "返回所有已启用的部署目标(如 k8s 集群、主机池)", + "produces": [ + "application/json" + ], + "tags": [ + "delivery" + ], + "summary": "获取可用部署目标", + "responses": { + "200": { + "description": "items: 部署目标数组", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "500": { + "description": "内部错误", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "管理员创建新的部署目标(目前仅支持 k8s 类型)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "delivery" + ], + "summary": "创建部署目标", + "parameters": [ + { + "description": "目标配置", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/handler.targetPayload" + } + } + ], + "responses": { + "201": { + "description": "目标已创建", + "schema": { + "$ref": "#/definitions/model.DeploymentTarget" + } + }, + "400": { + "description": "参数错误", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "401": { + "description": "未授权", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "409": { + "description": "名称冲突", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/auth/api/v1/delivery/tasks": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "返回当前用户可见的交付任务列表(管理员可见全部)", + "produces": [ + "application/json" + ], + "tags": [ + "delivery" + ], + "summary": "获取交付任务列表", + "parameters": [ + { + "type": "integer", + "description": "业务线 ID 过滤", + "name": "business_line_id", + "in": "query" + } + ], + "responses": { + "200": { + "description": "items: 任务数组", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "401": { + "description": "未授权", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/auth/api/v1/delivery/tasks/{id}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "返回指定任务的详细信息及事件流", + "produces": [ + "application/json" + ], + "tags": [ + "delivery" + ], + "summary": "获取交付任务详情", + "parameters": [ + { + "type": "string", + "description": "任务 ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "task + events", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "401": { + "description": "未授权", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "404": { + "description": "任务不存在", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/auth/api/v1/delivery/tasks/{id}/cancel": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "取消一个正在执行或等待中的交付任务", + "produces": [ + "application/json" + ], + "tags": [ + "delivery" + ], + "summary": "取消交付任务", + "parameters": [ + { + "type": "string", + "description": "任务 ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "ok: true", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "401": { + "description": "未授权", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "409": { + "description": "无法取消(状态冲突)", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, "/ping": { "get": { "description": "检查服务器是否正常运行", @@ -36,7 +387,7 @@ "schema": { "allOf": [ { - "$ref": "#/definitions/handler.Response" + "$ref": "#/definitions/response.Response" }, { "type": "object", @@ -74,7 +425,7 @@ "401": { "description": "Unauthorized", "schema": { - "$ref": "#/definitions/handler.Response" + "$ref": "#/definitions/response.Response" } } } @@ -97,7 +448,7 @@ "403": { "description": "Forbidden", "schema": { - "$ref": "#/definitions/handler.Response" + "$ref": "#/definitions/response.Response" } } } @@ -120,7 +471,7 @@ "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/handler.Response" + "$ref": "#/definitions/response.Response" } } } @@ -154,7 +505,7 @@ "schema": { "allOf": [ { - "$ref": "#/definitions/handler.Response" + "$ref": "#/definitions/response.Response" }, { "type": "object", @@ -170,7 +521,7 @@ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/handler.Response" + "$ref": "#/definitions/response.Response" } } } @@ -195,7 +546,7 @@ "schema": { "allOf": [ { - "$ref": "#/definitions/handler.Response" + "$ref": "#/definitions/response.Response" }, { "type": "object", @@ -203,7 +554,7 @@ "data": { "allOf": [ { - "$ref": "#/definitions/handler.PaginatedData" + "$ref": "#/definitions/response.PaginatedData" }, { "type": "object", @@ -245,7 +596,7 @@ "schema": { "allOf": [ { - "$ref": "#/definitions/handler.Response" + "$ref": "#/definitions/response.Response" }, { "type": "object", @@ -286,13 +637,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/handler.Response" + "$ref": "#/definitions/response.Response" } }, "408": { "description": "Request Timeout", "schema": { - "$ref": "#/definitions/handler.Response" + "$ref": "#/definitions/response.Response" } } } @@ -300,7 +651,129 @@ } }, "definitions": { - "handler.PaginatedData": { + "handler.quotaPayload": { + "type": "object", + "required": [ + "business_line_id", + "cpu_milli", + "instance_limit", + "memory_mi", + "storage_gi", + "target_id" + ], + "properties": { + "business_line_id": { + "type": "integer" + }, + "cpu_milli": { + "type": "integer" + }, + "instance_limit": { + "type": "integer" + }, + "memory_mi": { + "type": "integer" + }, + "storage_gi": { + "type": "integer" + }, + "target_id": { + "type": "integer" + } + } + }, + "handler.targetPayload": { + "type": "object", + "required": [ + "awx_inventory_id", + "awx_template_id", + "name", + "target_type" + ], + "properties": { + "awx_inventory_id": { + "type": "integer" + }, + "awx_template_id": { + "type": "integer" + }, + "metadata": { + "type": "object", + "additionalProperties": {} + }, + "name": { + "type": "string" + }, + "target_type": { + "type": "string" + } + } + }, + "model.DeploymentTarget": { + "type": "object", + "properties": { + "awx_inventory_id": { + "type": "integer" + }, + "awx_template_id": { + "type": "integer" + }, + "created_at": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "id": { + "type": "integer" + }, + "metadata": { + "type": "string" + }, + "name": { + "type": "string" + }, + "target_type": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + } + }, + "model.ResourceQuota": { + "type": "object", + "properties": { + "business_line_id": { + "type": "integer" + }, + "cpu_milli": { + "type": "integer" + }, + "created_at": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "instance_limit": { + "type": "integer" + }, + "memory_mi": { + "type": "integer" + }, + "storage_gi": { + "type": "integer" + }, + "target_id": { + "type": "integer" + }, + "updated_at": { + "type": "string" + } + } + }, + "response.PaginatedData": { "type": "object", "properties": { "items": {}, @@ -309,25 +782,58 @@ } } }, - "handler.Response": { + "response.Response": { "type": "object", "properties": { "code": { - "description": "业务错误码,0 表示成功", "type": "integer" }, - "data": { - "description": "响应数据,成功时返回" - }, + "data": {}, "details": { - "description": "错误详情,失败时返回", "type": "string" }, "message": { - "description": "响应消息", "type": "string" } } + }, + "service.MySQLDeliveryInput": { + "type": "object", + "required": [ + "business_line_id", + "cpu_milli", + "instance_name", + "memory_mi", + "namespace", + "storage_gi", + "target_id" + ], + "properties": { + "business_line_id": { + "type": "integer" + }, + "cpu_milli": { + "type": "integer" + }, + "instance_name": { + "type": "string" + }, + "memory_mi": { + "type": "integer" + }, + "mysql_version": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "storage_gi": { + "type": "integer" + }, + "target_id": { + "type": "integer" + } + } } }, "securityDefinitions": { diff --git a/server/docs/swagger.yaml b/server/docs/swagger.yaml index 183efc1..97d66ff 100644 --- a/server/docs/swagger.yaml +++ b/server/docs/swagger.yaml @@ -1,25 +1,131 @@ basePath: /api/v1 definitions: - handler.PaginatedData: + handler.quotaPayload: + properties: + business_line_id: + type: integer + cpu_milli: + type: integer + instance_limit: + type: integer + memory_mi: + type: integer + storage_gi: + type: integer + target_id: + type: integer + required: + - business_line_id + - cpu_milli + - instance_limit + - memory_mi + - storage_gi + - target_id + type: object + handler.targetPayload: + properties: + awx_inventory_id: + type: integer + awx_template_id: + type: integer + metadata: + additionalProperties: {} + type: object + name: + type: string + target_type: + type: string + required: + - awx_inventory_id + - awx_template_id + - name + - target_type + type: object + model.DeploymentTarget: + properties: + awx_inventory_id: + type: integer + awx_template_id: + type: integer + created_at: + type: string + enabled: + type: boolean + id: + type: integer + metadata: + type: string + name: + type: string + target_type: + type: string + updated_at: + type: string + type: object + model.ResourceQuota: + properties: + business_line_id: + type: integer + cpu_milli: + type: integer + created_at: + type: string + id: + type: integer + instance_limit: + type: integer + memory_mi: + type: integer + storage_gi: + type: integer + target_id: + type: integer + updated_at: + type: string + type: object + response.PaginatedData: properties: items: {} total: type: integer type: object - handler.Response: + response.Response: properties: code: - description: 业务错误码,0 表示成功 type: integer - data: - description: 响应数据,成功时返回 + data: {} details: - description: 错误详情,失败时返回 type: string message: - description: 响应消息 type: string type: object + service.MySQLDeliveryInput: + properties: + business_line_id: + type: integer + cpu_milli: + type: integer + instance_name: + type: string + memory_mi: + type: integer + mysql_version: + type: string + namespace: + type: string + storage_gi: + type: integer + target_id: + type: integer + required: + - business_line_id + - cpu_milli + - instance_name + - memory_mi + - namespace + - storage_gi + - target_id + type: object info: contact: email: support@swagger.io @@ -33,6 +139,234 @@ info: title: xinfra API version: "1.0" paths: + /auth/api/v1/delivery/mysql: + post: + consumes: + - application/json + description: 创建一个 MySQL 交付任务,调度器会自动分配主机、调用 AWX 执行部署 + parameters: + - description: 幂等键(防重复提交,最长 128 字符) + in: header + name: Idempotency-Key + required: true + type: string + - description: 交付参数 + in: body + name: body + required: true + schema: + $ref: '#/definitions/service.MySQLDeliveryInput' + produces: + - application/json + responses: + "200": + description: 幂等重放(相同 Idempotency-Key 已存在) + schema: + additionalProperties: true + type: object + "202": + description: 任务已创建 + schema: + additionalProperties: true + type: object + "400": + description: 参数错误 + schema: + additionalProperties: true + type: object + "401": + description: 未授权 + schema: + additionalProperties: true + type: object + security: + - BearerAuth: [] + summary: 提交 MySQL 一键交付 + tags: + - delivery + /auth/api/v1/delivery/quotas: + put: + consumes: + - application/json + description: 管理员为指定业务线 + 部署目标设置资源配额 + parameters: + - description: 配额参数 + in: body + name: body + required: true + schema: + $ref: '#/definitions/handler.quotaPayload' + produces: + - application/json + responses: + "200": + description: 配额已更新 + schema: + $ref: '#/definitions/model.ResourceQuota' + "400": + description: 参数错误 + schema: + additionalProperties: true + type: object + "401": + description: 未授权 + schema: + additionalProperties: true + type: object + security: + - BearerAuth: [] + summary: 创建或更新资源配额 + tags: + - delivery + /auth/api/v1/delivery/targets: + get: + description: 返回所有已启用的部署目标(如 k8s 集群、主机池) + produces: + - application/json + responses: + "200": + description: 'items: 部署目标数组' + schema: + additionalProperties: true + type: object + "500": + description: 内部错误 + schema: + additionalProperties: true + type: object + security: + - BearerAuth: [] + summary: 获取可用部署目标 + tags: + - delivery + post: + consumes: + - application/json + description: 管理员创建新的部署目标(目前仅支持 k8s 类型) + parameters: + - description: 目标配置 + in: body + name: body + required: true + schema: + $ref: '#/definitions/handler.targetPayload' + produces: + - application/json + responses: + "201": + description: 目标已创建 + schema: + $ref: '#/definitions/model.DeploymentTarget' + "400": + description: 参数错误 + schema: + additionalProperties: true + type: object + "401": + description: 未授权 + schema: + additionalProperties: true + type: object + "409": + description: 名称冲突 + schema: + additionalProperties: true + type: object + security: + - BearerAuth: [] + summary: 创建部署目标 + tags: + - delivery + /auth/api/v1/delivery/tasks: + get: + description: 返回当前用户可见的交付任务列表(管理员可见全部) + parameters: + - description: 业务线 ID 过滤 + in: query + name: business_line_id + type: integer + produces: + - application/json + responses: + "200": + description: 'items: 任务数组' + schema: + additionalProperties: true + type: object + "401": + description: 未授权 + schema: + additionalProperties: true + type: object + security: + - BearerAuth: [] + summary: 获取交付任务列表 + tags: + - delivery + /auth/api/v1/delivery/tasks/{id}: + get: + description: 返回指定任务的详细信息及事件流 + parameters: + - description: 任务 ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: task + events + schema: + additionalProperties: true + type: object + "401": + description: 未授权 + schema: + additionalProperties: true + type: object + "404": + description: 任务不存在 + schema: + additionalProperties: true + type: object + security: + - BearerAuth: [] + summary: 获取交付任务详情 + tags: + - delivery + /auth/api/v1/delivery/tasks/{id}/cancel: + post: + description: 取消一个正在执行或等待中的交付任务 + parameters: + - description: 任务 ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: 'ok: true' + schema: + additionalProperties: true + type: object + "401": + description: 未授权 + schema: + additionalProperties: true + type: object + "409": + description: 无法取消(状态冲突) + schema: + additionalProperties: true + type: object + security: + - BearerAuth: [] + summary: 取消交付任务 + tags: + - delivery /ping: get: consumes: @@ -45,7 +379,7 @@ paths: description: OK schema: allOf: - - $ref: '#/definitions/handler.Response' + - $ref: '#/definitions/response.Response' - properties: data: properties: @@ -67,7 +401,7 @@ paths: "401": description: Unauthorized schema: - $ref: '#/definitions/handler.Response' + $ref: '#/definitions/response.Response' summary: 测试 401 错误 tags: - 测试 @@ -82,7 +416,7 @@ paths: "403": description: Forbidden schema: - $ref: '#/definitions/handler.Response' + $ref: '#/definitions/response.Response' summary: 测试 403 错误 tags: - 测试 @@ -97,7 +431,7 @@ paths: "500": description: Internal Server Error schema: - $ref: '#/definitions/handler.Response' + $ref: '#/definitions/response.Response' summary: 测试 500 错误 tags: - 测试 @@ -119,7 +453,7 @@ paths: description: OK schema: allOf: - - $ref: '#/definitions/handler.Response' + - $ref: '#/definitions/response.Response' - properties: details: type: string @@ -127,7 +461,7 @@ paths: "400": description: Bad Request schema: - $ref: '#/definitions/handler.Response' + $ref: '#/definitions/response.Response' summary: 测试业务错误响应 tags: - 测试 @@ -143,11 +477,11 @@ paths: description: OK schema: allOf: - - $ref: '#/definitions/handler.Response' + - $ref: '#/definitions/response.Response' - properties: data: allOf: - - $ref: '#/definitions/handler.PaginatedData' + - $ref: '#/definitions/response.PaginatedData' - properties: items: items: @@ -170,7 +504,7 @@ paths: description: OK schema: allOf: - - $ref: '#/definitions/handler.Response' + - $ref: '#/definitions/response.Response' - properties: data: properties: @@ -194,11 +528,11 @@ paths: "200": description: OK schema: - $ref: '#/definitions/handler.Response' + $ref: '#/definitions/response.Response' "408": description: Request Timeout schema: - $ref: '#/definitions/handler.Response' + $ref: '#/definitions/response.Response' summary: 测试超时响应 tags: - 测试 diff --git a/server/go.mod b/server/go.mod index 7a9c1d3..49286f2 100644 --- a/server/go.mod +++ b/server/go.mod @@ -20,6 +20,7 @@ require ( github.com/bytedance/sonic v1.14.0 // indirect github.com/bytedance/sonic/loader v0.3.0 // indirect github.com/cloudwego/base64x v0.1.6 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d // indirect github.com/gabriel-vasile/mimetype v1.4.8 // indirect github.com/gin-contrib/sse v1.1.0 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect @@ -45,8 +46,11 @@ require ( github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/quic-go/qpack v0.5.1 // indirect github.com/quic-go/quic-go v0.54.0 // indirect + github.com/russross/blackfriday/v2 v2.0.1 // indirect + github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.3.0 // indirect + github.com/urfave/cli/v2 v2.3.0 // indirect go.uber.org/mock v0.5.0 // indirect golang.org/x/arch v0.20.0 // indirect golang.org/x/crypto v0.54.0 // indirect @@ -58,4 +62,5 @@ require ( golang.org/x/tools v0.47.0 // indirect google.golang.org/protobuf v1.36.9 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/server/go.sum b/server/go.sum index 4bd0797..48fc442 100644 --- a/server/go.sum +++ b/server/go.sum @@ -1,5 +1,6 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= @@ -12,6 +13,8 @@ github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZw github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -91,6 +94,10 @@ github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg= github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY= +github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -111,6 +118,8 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA= github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= +github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= +github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= @@ -167,6 +176,7 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= @@ -177,3 +187,5 @@ gorm.io/driver/mysql v1.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg= gorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo= gorm.io/gorm v1.30.1 h1:lSHg33jJTBxs2mgJRfRZeLDG+WZaHYCk3Wtfl6Ngzo4= gorm.io/gorm v1.30.1/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/server/internal/handler/test.go b/server/internal/handler/test.go index 1b3ca88..a240d62 100644 --- a/server/internal/handler/test.go +++ b/server/internal/handler/test.go @@ -17,7 +17,7 @@ import ( // @Tags 系统 // @Accept json // @Produce json -// @Success 200 {object} Response{data=object{time=string}} +// @Success 200 {object} response.Response{data=object{time=string}} // @Router /ping [get] func Ping(c *gin.Context) { response.Success(c, gin.H{"time": time.Now().Format(time.RFC3339)}) @@ -29,7 +29,7 @@ func Ping(c *gin.Context) { // @Tags 测试 // @Accept json // @Produce json -// @Success 200 {object} Response{data=object{username=string,role=string}} +// @Success 200 {object} response.Response{data=object{username=string,role=string}} // @Router /test/success [get] func TestSuccess(c *gin.Context) { response.Success(c, gin.H{ @@ -45,8 +45,8 @@ func TestSuccess(c *gin.Context) { // @Accept json // @Produce json // @Param code path int true "业务错误码 (10001-10099: 认证相关, 20001-20099: 任务相关)" -// @Success 200 {object} Response{details=string} -// @Failure 400 {object} Response +// @Success 200 {object} response.Response{details=string} +// @Failure 400 {object} response.Response // @Router /test/error/{code} [get] func TestError(c *gin.Context) { codeStr := c.Param("code") @@ -65,7 +65,7 @@ func TestError(c *gin.Context) { // @Tags 测试 // @Accept json // @Produce json -// @Success 500 {object} Response +// @Success 500 {object} response.Response // @Router /test/500 [get] func Test500(c *gin.Context) { response.InternalError(c, "模拟服务器内部错误") @@ -77,7 +77,7 @@ func Test500(c *gin.Context) { // @Tags 测试 // @Accept json // @Produce json -// @Success 401 {object} Response +// @Success 401 {object} response.Response // @Router /test/401 [get] func Test401(c *gin.Context) { response.Unauthorized(c, "模拟 Token 过期") @@ -89,7 +89,7 @@ func Test401(c *gin.Context) { // @Tags 测试 // @Accept json // @Produce json -// @Success 403 {object} Response +// @Success 403 {object} response.Response // @Router /test/403 [get] func Test403(c *gin.Context) { response.Forbidden(c, "模拟无权限") @@ -101,8 +101,8 @@ func Test403(c *gin.Context) { // @Tags 测试 // @Accept json // @Produce json -// @Success 200 {object} Response -// @Failure 408 {object} Response +// @Success 200 {object} response.Response +// @Failure 408 {object} response.Response // @Router /test/timeout [get] func TestTimeout(c *gin.Context) { time.Sleep(15 * time.Second) @@ -115,7 +115,7 @@ func TestTimeout(c *gin.Context) { // @Tags 测试 // @Accept json // @Produce json -// @Success 200 {object} Response{data=PaginatedData{items=[]object}} +// @Success 200 {object} response.Response{data=response.PaginatedData{items=[]object}} // @Router /test/paginated [get] func TestPaginated(c *gin.Context) { items := []gin.H{