Files
goproxy/examples/plugin/openapi_generator.go
DarkiT cc4c677553 增强插件系统:添加参数验证功能
- 在插件助手中新增 ValidateParams 函数,用于验证传入参数是否符合定义,确保参数的完整性和正确性。
- 更新 ExecuteAction 方法,集成参数验证逻辑,提升插件执行的安全性和可靠性。
- 示例程序中添加新的选项,展示如何生成插件API文档和OpenAPI/Swagger文档,增强用户体验。

此更新提升了插件系统的健壮性,便于开发者更好地管理和使用插件功能。
2025-03-14 13:32:36 +08:00

768 lines
21 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package plugin
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
)
// OpenAPIGenerator 用于生成符合OpenAPI 3.0规范的API文档
type OpenAPIGenerator struct {
pm *PluginManager // 插件管理器
outputPath string // 输出路径
info map[string]interface{}
}
// NewOpenAPIGenerator 创建一个新的OpenAPI生成器
func NewOpenAPIGenerator(pm *PluginManager, outputPath string) *OpenAPIGenerator {
return &OpenAPIGenerator{
pm: pm,
outputPath: outputPath,
info: map[string]interface{}{
"title": "插件系统API",
"description": "插件系统提供的API接口文档",
"version": "1.0.0",
"contact": map[string]interface{}{
"name": "开发团队",
"email": "dev@example.com",
"url": "https://example.com",
},
},
}
}
// SetInfo 设置API文档的基本信息
func (g *OpenAPIGenerator) SetInfo(title, description, version string) {
g.info["title"] = title
g.info["description"] = description
g.info["version"] = version
}
// SetContact 设置联系人信息
func (g *OpenAPIGenerator) SetContact(name, email, url string) {
g.info["contact"] = map[string]interface{}{
"name": name,
"email": email,
"url": url,
}
}
// GenerateOpenAPI 生成OpenAPI文档
func (g *OpenAPIGenerator) GenerateOpenAPI() error {
// 确保输出目录存在
if err := os.MkdirAll(g.outputPath, 0755); err != nil {
return fmt.Errorf("创建输出目录失败: %v", err)
}
// 创建OpenAPI规范文档
openapiSpec := g.createOpenAPISpec()
// 将文档保存为JSON文件
jsonPath := filepath.Join(g.outputPath, "openapi.json")
jsonData, err := json.MarshalIndent(openapiSpec, "", " ")
if err != nil {
return fmt.Errorf("JSON序列化失败: %v", err)
}
if err := os.WriteFile(jsonPath, jsonData, 0644); err != nil {
return fmt.Errorf("写入JSON文件失败: %v", err)
}
// 创建HTML查看器
htmlPath := filepath.Join(g.outputPath, "index.html")
htmlContent := g.createSwaggerUIHTML()
if err := os.WriteFile(htmlPath, []byte(htmlContent), 0644); err != nil {
return fmt.Errorf("写入HTML文件失败: %v", err)
}
return nil
}
// createOpenAPISpec 创建完整的OpenAPI规范文档
func (g *OpenAPIGenerator) createOpenAPISpec() map[string]interface{} {
// 基本结构
spec := map[string]interface{}{
"openapi": "3.0.0",
"info": g.info,
"servers": []map[string]interface{}{
{
"url": "/api",
"description": "API服务器",
},
},
"tags": []map[string]interface{}{},
"paths": map[string]interface{}{},
"components": map[string]interface{}{
"schemas": map[string]interface{}{},
"parameters": map[string]interface{}{},
},
}
// 获取所有插件
plugins := g.pm.GetAllPlugins()
// 生成标签(按插件类型分组)
pluginTypeMap := make(map[PluginType]bool)
for _, p := range plugins {
pluginTypeMap[p.Type()] = true
}
tags := make([]map[string]interface{}, 0)
for pType := range pluginTypeMap {
tags = append(tags, map[string]interface{}{
"name": string(pType),
"description": fmt.Sprintf("%s 类型的插件", pType),
})
}
spec["tags"] = tags
// 为每个插件生成路径
paths := make(map[string]interface{})
schemas := make(map[string]interface{})
for _, p := range plugins {
pluginName := p.Name()
pluginType := string(p.Type())
// 插件基本信息路径
pluginInfoPath := fmt.Sprintf("/plugins/{name}/info")
if _, exists := paths[pluginInfoPath]; !exists {
paths[pluginInfoPath] = map[string]interface{}{
"get": map[string]interface{}{
"summary": "获取插件信息",
"description": "获取指定插件的详细信息",
"operationId": "getPluginInfo",
"tags": []string{"Plugin"},
"parameters": []map[string]interface{}{
{
"name": "name",
"in": "path",
"description": "插件名称",
"required": true,
"schema": map[string]interface{}{
"type": "string",
},
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "成功",
"content": map[string]interface{}{
"application/json": map[string]interface{}{
"schema": map[string]interface{}{
"$ref": "#/components/schemas/PluginInfo",
},
},
},
},
"404": map[string]interface{}{
"description": "插件不存在",
},
},
},
}
}
// 定义插件操作的路径
operations := p.GetAllOperations()
for opName, opInfo := range operations {
pathTemplate := fmt.Sprintf("/plugins/{name}/execute/{action}")
// 如果路径不存在,创建路径
if _, exists := paths[pathTemplate]; !exists {
paths[pathTemplate] = map[string]interface{}{
"post": map[string]interface{}{
"summary": "执行插件操作",
"description": "对指定插件执行特定操作",
"operationId": "executePluginAction",
"tags": []string{"Plugin"},
"parameters": []map[string]interface{}{
{
"name": "name",
"in": "path",
"description": "插件名称",
"required": true,
"schema": map[string]interface{}{
"type": "string",
},
},
{
"name": "action",
"in": "path",
"description": "操作名称",
"required": true,
"schema": map[string]interface{}{
"type": "string",
},
},
},
"requestBody": map[string]interface{}{
"description": "操作参数",
"content": map[string]interface{}{
"application/json": map[string]interface{}{
"schema": map[string]interface{}{
"type": "object",
},
},
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "操作成功",
"content": map[string]interface{}{
"application/json": map[string]interface{}{
"schema": map[string]interface{}{
"type": "object",
},
},
},
},
"400": map[string]interface{}{
"description": "参数错误",
},
"404": map[string]interface{}{
"description": "插件或操作不存在",
},
"500": map[string]interface{}{
"description": "服务器错误",
},
},
},
}
}
// 为每个操作创建特定的路径
opPath := fmt.Sprintf("/plugins/%s/actions/%s", pluginName, opName)
opRequestSchema := g.createRequestSchema(opName, opInfo, schemas)
opResponseSchema := g.createResponseSchema(opName, opInfo, schemas)
paths[opPath] = map[string]interface{}{
"post": map[string]interface{}{
"summary": getMapString(opInfo, "description", fmt.Sprintf("执行 %s", opName)),
"description": getMapString(opInfo, "description", fmt.Sprintf("在 %s 插件上执行 %s 操作", pluginName, opName)),
"operationId": fmt.Sprintf("%s_%s", pluginName, opName),
"tags": []string{pluginType},
"requestBody": map[string]interface{}{
"description": "操作参数",
"content": map[string]interface{}{
"application/json": map[string]interface{}{
"schema": opRequestSchema,
},
},
"required": true,
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "操作成功",
"content": map[string]interface{}{
"application/json": map[string]interface{}{
"schema": opResponseSchema,
},
},
},
"400": map[string]interface{}{
"description": "参数错误",
},
"500": map[string]interface{}{
"description": "服务器错误",
},
},
},
}
}
}
// 添加通用路径
// 获取所有插件
paths["/plugins"] = map[string]interface{}{
"get": map[string]interface{}{
"summary": "获取所有插件",
"description": "获取系统中所有可用插件的列表",
"operationId": "getAllPlugins",
"tags": []string{"PluginSystem"},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "成功",
"content": map[string]interface{}{
"application/json": map[string]interface{}{
"schema": map[string]interface{}{
"type": "array",
"items": map[string]interface{}{
"$ref": "#/components/schemas/PluginInfo",
},
},
},
},
},
},
},
}
// 按类型获取插件
paths["/plugins/type/{type}"] = map[string]interface{}{
"get": map[string]interface{}{
"summary": "按类型获取插件",
"description": "获取指定类型的所有插件",
"operationId": "getPluginsByType",
"tags": []string{"PluginSystem"},
"parameters": []map[string]interface{}{
{
"name": "type",
"in": "path",
"description": "插件类型",
"required": true,
"schema": map[string]interface{}{
"type": "string",
"enum": []string{
string(PluginTypeGeneral),
string(PluginTypeStorage),
string(PluginTypeUtils),
string(PluginTypeNetwork),
},
},
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "成功",
"content": map[string]interface{}{
"application/json": map[string]interface{}{
"schema": map[string]interface{}{
"type": "array",
"items": map[string]interface{}{
"$ref": "#/components/schemas/PluginInfo",
},
},
},
},
},
},
},
}
// 获取插件所有操作
paths["/plugins/{name}/actions"] = map[string]interface{}{
"get": map[string]interface{}{
"summary": "获取插件所有操作",
"description": "获取指定插件支持的所有操作",
"operationId": "getPluginActions",
"tags": []string{"PluginSystem"},
"parameters": []map[string]interface{}{
{
"name": "name",
"in": "path",
"description": "插件名称",
"required": true,
"schema": map[string]interface{}{
"type": "string",
},
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "成功",
"content": map[string]interface{}{
"application/json": map[string]interface{}{
"schema": map[string]interface{}{
"type": "object",
"additionalProperties": map[string]interface{}{
"type": "object",
},
},
},
},
},
"404": map[string]interface{}{
"description": "插件不存在",
},
},
},
}
// 设置路径和组件
spec["paths"] = paths
// 添加基本模式
schemas["PluginInfo"] = map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"name": map[string]interface{}{
"type": "string",
"description": "插件名称",
},
"version": map[string]interface{}{
"type": "string",
"description": "插件版本",
},
"description": map[string]interface{}{
"type": "string",
"description": "插件描述",
},
"author": map[string]interface{}{
"type": "string",
"description": "插件作者",
},
"type": map[string]interface{}{
"type": "string",
"description": "插件类型",
"enum": []string{
string(PluginTypeGeneral),
string(PluginTypeStorage),
string(PluginTypeUtils),
string(PluginTypeNetwork),
},
},
"enabled": map[string]interface{}{
"type": "boolean",
"description": "插件是否启用",
},
},
"required": []string{"name", "version", "type", "enabled"},
}
// 更新组件模式
spec["components"].(map[string]interface{})["schemas"] = schemas
return spec
}
// createRequestSchema 创建操作请求的JSON Schema
func (g *OpenAPIGenerator) createRequestSchema(opName string, opInfo map[string]interface{}, schemas map[string]interface{}) map[string]interface{} {
schemaName := fmt.Sprintf("%sRequest", opName)
// 如果没有参数,返回空对象模式
params, ok := opInfo["parameters"].(map[string]interface{})
if !ok || len(params) == 0 {
return map[string]interface{}{
"type": "object",
}
}
// 创建请求模式
schema := map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{},
"required": []string{},
}
properties := schema["properties"].(map[string]interface{})
required := schema["required"].([]string)
// 添加参数
for paramName, paramInfo := range params {
if info, ok := paramInfo.(map[string]interface{}); ok {
paramType := getMapString(info, "type", "string")
paramDesc := getMapString(info, "description", "")
paramRequired := getMapBool(info, "required", false)
// 为不同类型创建不同的模式
var propSchema map[string]interface{}
switch paramType {
case "string":
propSchema = map[string]interface{}{
"type": "string",
"description": paramDesc,
}
case "integer":
propSchema = map[string]interface{}{
"type": "integer",
"description": paramDesc,
}
case "float":
propSchema = map[string]interface{}{
"type": "number",
"description": paramDesc,
}
case "boolean":
propSchema = map[string]interface{}{
"type": "boolean",
"description": paramDesc,
}
case "array":
propSchema = map[string]interface{}{
"type": "array",
"description": paramDesc,
"items": map[string]interface{}{
"type": "object",
},
}
// 如果有元素类型信息,使用它
if elemType, ok := info["elemType"].(map[string]interface{}); ok {
elemTypeStr := getMapString(elemType, "type", "string")
propSchema["items"] = map[string]interface{}{
"type": g.mapTypeToOpenAPI(elemTypeStr),
}
}
case "object", "struct":
// 对于结构体和对象,创建一个引用
structName := fmt.Sprintf("%s_%s", schemaName, paramName)
propSchema = map[string]interface{}{
"$ref": fmt.Sprintf("#/components/schemas/%s", structName),
"description": paramDesc,
}
// 创建结构体模式
structSchema := map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{},
"required": []string{},
}
// 添加结构体字段
if fields, ok := info["fields"].(map[string]interface{}); ok {
structProps := structSchema["properties"].(map[string]interface{})
structReq := structSchema["required"].([]string)
for fieldName, fieldInfo := range fields {
if fi, ok := fieldInfo.(map[string]interface{}); ok {
fieldType := getMapString(fi, "type", "string")
fieldDesc := getMapString(fi, "description", "")
fieldRequired := getMapBool(fi, "required", false)
structProps[fieldName] = map[string]interface{}{
"type": g.mapTypeToOpenAPI(fieldType),
"description": fieldDesc,
}
if fieldRequired {
structReq = append(structReq, fieldName)
}
}
}
structSchema["required"] = structReq
}
// 添加到模式
schemas[structName] = structSchema
default:
propSchema = map[string]interface{}{
"type": "string",
"description": paramDesc,
}
}
properties[paramName] = propSchema
if paramRequired {
required = append(required, paramName)
}
}
}
// 如果没有必填参数删除required数组
if len(required) == 0 {
delete(schema, "required")
} else {
schema["required"] = required
}
// 添加到模式
schemas[schemaName] = schema
return map[string]interface{}{
"$ref": fmt.Sprintf("#/components/schemas/%s", schemaName),
}
}
// createResponseSchema 创建操作响应的JSON Schema
func (g *OpenAPIGenerator) createResponseSchema(opName string, opInfo map[string]interface{}, schemas map[string]interface{}) map[string]interface{} {
schemaName := fmt.Sprintf("%sResponse", opName)
// 如果没有返回值,返回空对象模式
returns, ok := opInfo["returns"].(map[string]interface{})
if !ok || len(returns) == 0 {
return map[string]interface{}{
"type": "object",
}
}
// 如果只有一个错误返回,使用简单响应
if len(returns) == 1 && returns["error"] != nil {
return map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"success": map[string]interface{}{
"type": "boolean",
"description": "操作是否成功",
},
"error": map[string]interface{}{
"type": "string",
"description": "错误信息",
"nullable": true,
},
},
}
}
// 创建响应模式
schema := map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"success": map[string]interface{}{
"type": "boolean",
"description": "操作是否成功",
},
},
}
properties := schema["properties"].(map[string]interface{})
// 遍历所有返回值
for retName, retInfo := range returns {
if retName == "error" {
properties["error"] = map[string]interface{}{
"type": "string",
"description": "错误信息",
"nullable": true,
}
continue
}
if info, ok := retInfo.(map[string]interface{}); ok {
retType := getMapString(info, "type", "string")
retDesc := getMapString(info, "description", "")
// 为不同类型创建不同的模式
var propSchema map[string]interface{}
switch retType {
case "string":
propSchema = map[string]interface{}{
"type": "string",
"description": retDesc,
}
case "integer":
propSchema = map[string]interface{}{
"type": "integer",
"description": retDesc,
}
case "float":
propSchema = map[string]interface{}{
"type": "number",
"description": retDesc,
}
case "boolean":
propSchema = map[string]interface{}{
"type": "boolean",
"description": retDesc,
}
case "array":
propSchema = map[string]interface{}{
"type": "array",
"description": retDesc,
"items": map[string]interface{}{
"type": "object",
},
}
// 如果有元素类型信息,使用它
if elemType, ok := info["elemType"].(map[string]interface{}); ok {
elemTypeStr := getMapString(elemType, "type", "string")
propSchema["items"] = map[string]interface{}{
"type": g.mapTypeToOpenAPI(elemTypeStr),
}
}
case "object":
// 对于对象,创建嵌套模式
propSchema = map[string]interface{}{
"type": "object",
"description": retDesc,
"properties": map[string]interface{}{},
}
// 如果有属性信息,添加它们
if props, ok := info["properties"].(map[string]interface{}); ok {
propsMap := propSchema["properties"].(map[string]interface{})
for propName, propInfo := range props {
if pi, ok := propInfo.(map[string]interface{}); ok {
propType := getMapString(pi, "type", "string")
propDesc := getMapString(pi, "description", "")
propsMap[propName] = map[string]interface{}{
"type": g.mapTypeToOpenAPI(propType),
"description": propDesc,
}
}
}
}
default:
propSchema = map[string]interface{}{
"type": "string",
"description": retDesc,
}
}
properties[retName] = propSchema
}
}
// 添加到模式
schemas[schemaName] = schema
return map[string]interface{}{
"$ref": fmt.Sprintf("#/components/schemas/%s", schemaName),
}
}
// createSwaggerUIHTML 创建包含Swagger UI的HTML文件
func (g *OpenAPIGenerator) createSwaggerUIHTML() string {
return `<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>插件系统API文档</title>
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@4.5.0/swagger-ui.css" >
<style>
html { box-sizing: border-box; overflow: -moz-scrollbars-vertical; overflow-y: scroll; }
*, *:before, *:after { box-sizing: inherit; }
body { margin: 0; background: #fafafa; }
</style>
</head>
<body>
<div id="swagger-ui"></div>
<script src="https://cdn.jsdelivr.net/npm/swagger-ui-dist@4.5.0/swagger-ui-bundle.js"></script>
<script src="https://cdn.jsdelivr.net/npm/swagger-ui-dist@4.5.0/swagger-ui-standalone-preset.js"></script>
<script>
window.onload = function() {
const ui = SwaggerUIBundle({
url: "openapi.json",
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout"
});
window.ui = ui;
};
</script>
</body>
</html>`
}
// mapTypeToOpenAPI 将内部类型映射到OpenAPI类型
func (g *OpenAPIGenerator) mapTypeToOpenAPI(internalType string) string {
switch internalType {
case "integer":
return "integer"
case "float":
return "number"
case "boolean":
return "boolean"
case "array":
return "array"
case "object", "struct":
return "object"
default:
return "string"
}
}