diff --git a/controller/api/api.go b/controller/api/api.go deleted file mode 100644 index c1c2d0fd..00000000 --- a/controller/api/api.go +++ /dev/null @@ -1,43 +0,0 @@ -package api - -import ( - "reflect" - - "github.com/gin-gonic/gin" - - "github.com/eolinker/go-common/autowire" - - api_dto "github.com/APIParkLab/APIPark/module/api/dto" -) - -type IAPIController interface { - // Detail 获取API详情 - Detail(ctx *gin.Context, serviceId string, apiId string) (*api_dto.ApiDetail, error) - // SimpleDetail 获取API简要详情 - SimpleDetail(ctx *gin.Context, serviceId string, apiId string) (*api_dto.ApiSimpleDetail, error) - // Search 获取API列表 - Search(ctx *gin.Context, keyword string, serviceId string) ([]*api_dto.ApiItem, error) - // SimpleSearch 获取API简要列表 - SimpleSearch(ctx *gin.Context, keyword string, serviceId string) ([]*api_dto.ApiSimpleItem, error) - //SimpleList(ctx *gin.Context, serviceId string) ([]*api_dto.ApiSimpleItem, error) - // Create 创建API - Create(ctx *gin.Context, serviceId string, dto *api_dto.CreateApi) (*api_dto.ApiSimpleDetail, error) - // Edit 编辑API - Edit(ctx *gin.Context, serviceId string, apiId string, dto *api_dto.EditApi) (*api_dto.ApiSimpleDetail, error) - // Delete 删除API - Delete(ctx *gin.Context, serviceId string, apiId string) error - // Copy 复制API - Copy(ctx *gin.Context, serviceId string, apiId string, dto *api_dto.CreateApi) (*api_dto.ApiSimpleDetail, error) - // ApiDocDetail 获取API文档详情 - ApiDocDetail(ctx *gin.Context, serviceId string, apiId string) (*api_dto.ApiDocDetail, error) - // ApiProxyDetail 获取API代理详情 - ApiProxyDetail(ctx *gin.Context, serviceId string, apiId string) (*api_dto.ApiProxyDetail, error) - // Prefix 获取API前缀 - Prefix(ctx *gin.Context, serviceId string) (string, bool, error) -} - -func init() { - autowire.Auto[IAPIController](func() reflect.Value { - return reflect.ValueOf(new(imlAPIController)) - }) -} diff --git a/controller/api/iml.go b/controller/api/iml.go deleted file mode 100644 index 78a021f0..00000000 --- a/controller/api/iml.go +++ /dev/null @@ -1,65 +0,0 @@ -package api - -import ( - "github.com/APIParkLab/APIPark/module/api" - api_dto "github.com/APIParkLab/APIPark/module/api/dto" - "github.com/gin-gonic/gin" -) - -var _ IAPIController = (*imlAPIController)(nil) - -type imlAPIController struct { - module api.IApiModule `autowired:""` -} - -//func (i *imlAPIController) SimpleList(ctx *gin.Context, serviceId string) ([]*api_dto.ApiSimpleItem, error) { -// return i.module.SimpleList(ctx, serviceId) -//} - -func (i *imlAPIController) Detail(ctx *gin.Context, serviceId string, apiId string) (*api_dto.ApiDetail, error) { - return i.module.Detail(ctx, serviceId, apiId) -} - -func (i *imlAPIController) SimpleDetail(ctx *gin.Context, serviceId string, apiId string) (*api_dto.ApiSimpleDetail, error) { - return i.module.SimpleDetail(ctx, serviceId, apiId) -} - -func (i *imlAPIController) Search(ctx *gin.Context, keyword string, serviceId string) ([]*api_dto.ApiItem, error) { - return i.module.Search(ctx, keyword, serviceId) -} - -func (i *imlAPIController) SimpleSearch(ctx *gin.Context, keyword string, serviceId string) ([]*api_dto.ApiSimpleItem, error) { - return i.module.SimpleSearch(ctx, keyword, serviceId) -} - -func (i *imlAPIController) Create(ctx *gin.Context, serviceId string, dto *api_dto.CreateApi) (*api_dto.ApiSimpleDetail, error) { - return i.module.Create(ctx, serviceId, dto) -} - -func (i *imlAPIController) Edit(ctx *gin.Context, serviceId string, apiId string, dto *api_dto.EditApi) (*api_dto.ApiSimpleDetail, error) { - return i.module.Edit(ctx, serviceId, apiId, dto) -} - -func (i *imlAPIController) Delete(ctx *gin.Context, serviceId string, apiId string) error { - return i.module.Delete(ctx, serviceId, apiId) -} - -func (i *imlAPIController) Copy(ctx *gin.Context, serviceId string, apiId string, dto *api_dto.CreateApi) (*api_dto.ApiSimpleDetail, error) { - return i.module.Copy(ctx, serviceId, apiId, dto) -} - -func (i *imlAPIController) ApiDocDetail(ctx *gin.Context, serviceId string, apiId string) (*api_dto.ApiDocDetail, error) { - return i.module.ApiDocDetail(ctx, serviceId, apiId) -} - -func (i *imlAPIController) ApiProxyDetail(ctx *gin.Context, serviceId string, apiId string) (*api_dto.ApiProxyDetail, error) { - return i.module.ApiProxyDetail(ctx, serviceId, apiId) -} - -func (i *imlAPIController) Prefix(ctx *gin.Context, serviceId string) (string, bool, error) { - prefix, err := i.module.Prefix(ctx, serviceId) - if err != nil { - return "", false, err - } - return prefix, true, nil -} diff --git a/controller/monitor/iml.go b/controller/monitor/iml.go index da303824..26cdbb9e 100644 --- a/controller/monitor/iml.go +++ b/controller/monitor/iml.go @@ -75,3 +75,7 @@ func (p *imlMonitorConfig) SaveMonitorConfig(ctx *gin.Context, cfg *monitor_dto. func (p *imlMonitorConfig) GetMonitorConfig(ctx *gin.Context) (*monitor_dto.MonitorConfig, error) { return p.module.GetMonitorConfig(ctx) } + +func (p *imlMonitorConfig) GetMonitorCluster(ctx *gin.Context) ([]*monitor_dto.MonitorCluster, error) { + return p.module.GetMonitorCluster(ctx) +} diff --git a/controller/monitor/statistic.go b/controller/monitor/statistic.go index 019c0370..33624b87 100644 --- a/controller/monitor/statistic.go +++ b/controller/monitor/statistic.go @@ -22,6 +22,7 @@ type IMonitorStatisticController interface { type IMonitorConfigController interface { SaveMonitorConfig(ctx *gin.Context, cfg *monitor_dto.SaveMonitorConfig) (*monitor_dto.MonitorConfig, error) GetMonitorConfig(ctx *gin.Context) (*monitor_dto.MonitorConfig, error) + GetMonitorCluster(ctx *gin.Context) ([]*monitor_dto.MonitorCluster, error) } func init() { diff --git a/controller/publish/iml.go b/controller/publish/iml.go index 3e14ddda..ded2386b 100644 --- a/controller/publish/iml.go +++ b/controller/publish/iml.go @@ -2,7 +2,7 @@ package publish import ( "strconv" - + "github.com/APIParkLab/APIPark/module/publish" "github.com/APIParkLab/APIPark/module/publish/dto" "github.com/APIParkLab/APIPark/module/release" @@ -39,11 +39,11 @@ func (c *imlPublishController) ReleaseDo(ctx *gin.Context, serviceId string, inp c.releaseModule.Delete(ctx, serviceId, newReleaseId) return nil, err } - err = c.publishModule.Publish(ctx, serviceId, apply.Id) - if err != nil { - c.releaseModule.Delete(ctx, serviceId, newReleaseId) - return nil, err - } + //err = c.publishModule.Publish(ctx, serviceId, apply.Id) + //if err != nil { + // c.releaseModule.Delete(ctx, serviceId, newReleaseId) + // return nil, err + //} err = c.publishModule.Publish(ctx, serviceId, apply.Id) if err != nil { c.releaseModule.Delete(ctx, serviceId, newReleaseId) @@ -123,7 +123,7 @@ func (c *imlPublishController) ListPage(ctx *gin.Context, serviceId string, page if err != nil { return nil, 0, 0, 0, err } - + return list, pageNum, pageSizeNum, total, nil } diff --git a/controller/router/iml.go b/controller/router/iml.go new file mode 100644 index 00000000..d4aa2023 --- /dev/null +++ b/controller/router/iml.go @@ -0,0 +1,79 @@ +package router + +import ( + api_doc "github.com/APIParkLab/APIPark/module/api-doc" + api_doc_dto "github.com/APIParkLab/APIPark/module/api-doc/dto" + "github.com/APIParkLab/APIPark/module/router" + router_dto "github.com/APIParkLab/APIPark/module/router/dto" + "github.com/gin-gonic/gin" + "io" +) + +var _ IRouterController = (*imlAPIController)(nil) + +type imlAPIController struct { + module router.IRouterModule `autowired:""` +} + +func (i *imlAPIController) Detail(ctx *gin.Context, serviceId string, apiId string) (*router_dto.Detail, error) { + return i.module.Detail(ctx, serviceId, apiId) +} + +func (i *imlAPIController) Search(ctx *gin.Context, keyword string, serviceId string) ([]*router_dto.Item, error) { + return i.module.Search(ctx, keyword, serviceId) +} + +func (i *imlAPIController) Create(ctx *gin.Context, serviceId string, dto *router_dto.Create) (*router_dto.SimpleDetail, error) { + return i.module.Create(ctx, serviceId, dto) +} + +func (i *imlAPIController) Edit(ctx *gin.Context, serviceId string, apiId string, dto *router_dto.Edit) (*router_dto.SimpleDetail, error) { + return i.module.Edit(ctx, serviceId, apiId, dto) +} + +func (i *imlAPIController) Delete(ctx *gin.Context, serviceId string, apiId string) error { + return i.module.Delete(ctx, serviceId, apiId) +} + +func (i *imlAPIController) Prefix(ctx *gin.Context, serviceId string) (string, bool, error) { + prefix, err := i.module.Prefix(ctx, serviceId) + if err != nil { + return "", false, err + } + return prefix, true, nil +} + +var _ IAPIDocController = (*imlAPIDocController)(nil) + +type imlAPIDocController struct { + module api_doc.IAPIDocModule `autowired:""` +} + +func (i *imlAPIDocController) UpdateDoc(ctx *gin.Context, serviceId string, input *api_doc_dto.UpdateDoc) (*api_doc_dto.ApiDocDetail, error) { + return i.module.UpdateDoc(ctx, serviceId, input) +} + +func (i *imlAPIDocController) GetDoc(ctx *gin.Context, serviceId string) (*api_doc_dto.ApiDocDetail, error) { + return i.module.GetDoc(ctx, serviceId) +} + +func (i *imlAPIDocController) UploadDoc(ctx *gin.Context, serviceId string) (*api_doc_dto.ApiDocDetail, error) { + // 获取文件内容 + fileHeader, err := ctx.FormFile("doc") + if err != nil { + return nil, err + } + + file, err := fileHeader.Open() + if err != nil { + return nil, err + } + content, err := io.ReadAll(file) + if err != nil { + return nil, err + } + + return i.module.UpdateDoc(ctx, serviceId, &api_doc_dto.UpdateDoc{ + Content: string(content), + }) +} diff --git a/controller/router/router.go b/controller/router/router.go new file mode 100644 index 00000000..a406947e --- /dev/null +++ b/controller/router/router.go @@ -0,0 +1,46 @@ +package router + +import ( + api_doc_dto "github.com/APIParkLab/APIPark/module/api-doc/dto" + "reflect" + + "github.com/gin-gonic/gin" + + "github.com/eolinker/go-common/autowire" + + router_dto "github.com/APIParkLab/APIPark/module/router/dto" +) + +type IRouterController interface { + // Detail 获取API详情 + Detail(ctx *gin.Context, serviceId string, apiId string) (*router_dto.Detail, error) + // Search 获取API列表 + Search(ctx *gin.Context, keyword string, serviceId string) ([]*router_dto.Item, error) + // Create 创建API + Create(ctx *gin.Context, serviceId string, dto *router_dto.Create) (*router_dto.SimpleDetail, error) + // Edit 编辑API + Edit(ctx *gin.Context, serviceId string, apiId string, dto *router_dto.Edit) (*router_dto.SimpleDetail, error) + // Delete 删除API + Delete(ctx *gin.Context, serviceId string, apiId string) error + // Prefix 获取API前缀 + Prefix(ctx *gin.Context, serviceId string) (string, bool, error) +} + +type IAPIDocController interface { + // UpdateDoc 更新API文档 + UpdateDoc(ctx *gin.Context, serviceId string, input *api_doc_dto.UpdateDoc) (*api_doc_dto.ApiDocDetail, error) + // GetDoc 获取API文档 + GetDoc(ctx *gin.Context, serviceId string) (*api_doc_dto.ApiDocDetail, error) + + UploadDoc(ctx *gin.Context, serviceId string) (*api_doc_dto.ApiDocDetail, error) +} + +func init() { + autowire.Auto[IRouterController](func() reflect.Value { + return reflect.ValueOf(new(imlAPIController)) + }) + + autowire.Auto[IAPIDocController](func() reflect.Value { + return reflect.ValueOf(new(imlAPIDocController)) + }) +} diff --git a/controller/system/config/api.json b/controller/system/config/api.json new file mode 100644 index 00000000..3d31a516 --- /dev/null +++ b/controller/system/config/api.json @@ -0,0 +1 @@ +[{"id":"d4a73cdb-f7da-437d-85a3-fabb4fb6bd0b","name":"Data Cleaning API","description":"Cleans raw data by removing duplicates, correcting formatting issues, and standardizing values.","method":"POST","path":"/clean","match":null,"service":"2d8beb9c-121f-4f6c-b860-50c02520e6f3","team":"eef18cfb-140b-4e78-b9eb-ad669d898110","proxy":{"path":"api/data/clean","timeout":10000,"retry":0,"headers":[],"extends":null,"plugins":null},"doc":{"requestParams":{"bodyParams":[{"childList":[],"contentType":2,"dataType":13,"description":"","id":"o4ab0ox4","isRequired":1,"name":"data","orderNo":0,"paramAttr":{"example":""}}],"headerParams":[],"queryParams":[],"restParams":[]},"responseList":[{"httpCode":2,"id":"0fb302b7-e9e3-4b84-b30d-740566135afb","responseParams":{"bodyParams":[{"childList":[],"dataType":13,"description":"","id":"5zecekmf","isRequired":1,"name":"cleaned_data","orderNo":0,"paramAttr":{"example":""}}],"headerParams":[]},"responseUuid":"0fb302b7-e9e3-4b84-b30d-740566135afb"}],"resultList":[{"content":"{\r\n \"cleaned_data\": [\r\n {\r\n \"name\": \"John\",\r\n \"age\": 30,\r\n \"email\": \"john.doe@example.com\"\r\n },\r\n {\r\n \"name\": \"Jane\",\r\n \"age\": 25,\r\n \"email\": \"jane.doe@example.com\"\r\n }\r\n ]\r\n}","httpCode":"200","id":"success","name":"成功示例"},{"content":"","httpCode":"200","id":"failed","name":"失败示例"}]}},{"id":"7f650281-0b81-41bb-b31a-92dc7178fe1c","name":"Data Transformation API","description":"Transforms data based on specified operations such as normalization, aggregation, or enrichment.","method":"POST","path":"/transform","match":null,"service":"2d8beb9c-121f-4f6c-b860-50c02520e6f3","team":"eef18cfb-140b-4e78-b9eb-ad669d898110","proxy":{"path":"api/data/transform","timeout":10000,"retry":0,"headers":[],"extends":null,"plugins":null},"doc":{"requestParams":{"bodyParams":[{"childList":[],"contentType":2,"dataType":13,"description":"","id":"f2ey3c2l","isRequired":1,"name":"data","orderNo":0,"paramAttr":{"example":""}},{"childList":[],"contentType":2,"dataType":13,"description":"","id":"fv87rv0r","isRequired":1,"name":"operations","orderNo":0,"paramAttr":{"example":""}}],"headerParams":[],"queryParams":[],"restParams":[]},"responseList":[{"httpCode":2,"id":"8112fcea-31a0-4cf8-8515-113812c5fc4c","responseParams":{"bodyParams":[{"childList":[],"dataType":0,"description":"","id":"ccn7jc5m","isRequired":1,"name":"transformed_data","orderNo":0,"paramAttr":{"example":""}}],"headerParams":[]},"responseUuid":"8112fcea-31a0-4cf8-8515-113812c5fc4c"}],"resultList":[{"content":"{\r\n \"transformed_data\": [\r\n {\r\n \"name\": \"John\",\r\n \"age\": 0.75\r\n },\r\n {\r\n \"name\": \"Jane\",\r\n \"age\": 0.5\r\n }\r\n ],\r\n \"aggregates\": {\r\n \"average_age\": 0.625\r\n }\r\n}","httpCode":"200","id":"success","name":"成功示例"},{"content":"","httpCode":"200","id":"failed","name":"失败示例"}]}},{"id":"868228a2-a756-4181-80b7-0612c078e61f","name":"Data Analysis API","description":"Analyzes data using statistical methods, generating insights and visualizations.","method":"POST","path":"/analyze","match":null,"service":"2d8beb9c-121f-4f6c-b860-50c02520e6f3","team":"eef18cfb-140b-4e78-b9eb-ad669d898110","proxy":{"path":"api/data/analyze","timeout":10000,"retry":0,"headers":[],"extends":null,"plugins":null},"doc":{"requestParams":{"bodyParams":[{"childList":[],"contentType":2,"dataType":0,"description":"","id":"i308tt8b","isRequired":1,"name":"data","orderNo":0,"paramAttr":{"example":""}},{"childList":[],"contentType":2,"dataType":0,"description":"","id":"tcqaydlf","isRequired":1,"name":"analysis_type","orderNo":0,"paramAttr":{"example":"statistical_summary"}},{"childList":[{"childList":[],"dataType":0,"description":"","id":"itr5gnfw","isRequired":1,"name":"include_charts","orderNo":1,"paramAttr":{"example":"true"}}],"contentType":2,"dataType":13,"description":"","id":"59r9jf9b","isRequired":1,"name":"parameters","orderNo":0,"paramAttr":{"example":""}}],"headerParams":[],"queryParams":[],"restParams":[]},"responseList":[{"httpCode":2,"id":"add187f5-c3af-4e48-86df-9ee337a19c9d","responseParams":{"bodyParams":[{"childList":[{"childList":[],"dataType":0,"description":"","id":"q3iul2bg","isRequired":1,"name":"mean_sales","orderNo":1,"paramAttr":{"example":""}},{"childList":[],"dataType":0,"description":"","id":"br5ez8kf","isRequired":1,"name":"median_sales","orderNo":1,"paramAttr":{"example":""}},{"childList":[],"dataType":0,"description":"","id":"90ywigrm","isRequired":1,"name":"standard_deviation","orderNo":1,"paramAttr":{"example":""}}],"dataType":13,"description":"","id":"js055oqp","isRequired":1,"name":"summary","orderNo":0,"paramAttr":{"example":""}},{"childList":[{"childList":[],"dataType":0,"description":"","id":"lfbacm4f","isRequired":1,"name":"bar_chart_url","orderNo":1,"paramAttr":{"example":"https://example.com/charts/sales_bar_chart.png"}}],"dataType":13,"description":"","id":"ro9bejbp","isRequired":1,"name":"charts","orderNo":0,"paramAttr":{"example":""}}],"headerParams":[]},"responseUuid":"add187f5-c3af-4e48-86df-9ee337a19c9d"}],"resultList":[{"content":"{\r\n \"summary\": {\r\n \"mean_sales\": 216.67,\r\n \"median_sales\": 200,\r\n \"standard_deviation\": 75.76\r\n },\r\n \"charts\": {\r\n \"bar_chart_url\": \"https://example.com/charts/sales_bar_chart.png\"\r\n }\r\n}","httpCode":"200","id":"success","name":"成功示例"},{"content":"","httpCode":"200","id":"failed","name":"失败示例"}]}}] \ No newline at end of file diff --git a/controller/system/config/app.json b/controller/system/config/app.json new file mode 100644 index 00000000..d9c447b6 --- /dev/null +++ b/controller/system/config/app.json @@ -0,0 +1 @@ +[{"id":"358caa9f-a079-44dd-8b28-c1d065777b6c","name":"Demo App","description":"Create the App to apply services","team":"eef18cfb-140b-4e78-b9eb-ad669d898110"}] \ No newline at end of file diff --git a/controller/system/config/apply.json b/controller/system/config/apply.json new file mode 100644 index 00000000..049d45ce --- /dev/null +++ b/controller/system/config/apply.json @@ -0,0 +1 @@ +[{"service":"2d8beb9c-121f-4f6c-b860-50c02520e6f3","application":"358caa9f-a079-44dd-8b28-c1d065777b6c","reason":""}] \ No newline at end of file diff --git a/controller/system/config/authorization.json b/controller/system/config/authorization.json new file mode 100644 index 00000000..05843fce --- /dev/null +++ b/controller/system/config/authorization.json @@ -0,0 +1 @@ +[{"application":"358caa9f-a079-44dd-8b28-c1d065777b6c","id":"9179f789-869b-40a5-8e45-d0c85c640f29","name":"Demo Auth Key","driver":"apikey","position":"Header","token_name":"Authorization","config":{"apikey":"da66c011-39ba-44eb-a318-3f1407644543","label":null},"expire_time":0,"hide_credential":false}] \ No newline at end of file diff --git a/controller/system/config/catalogue.json b/controller/system/config/catalogue.json new file mode 100644 index 00000000..46b46a16 --- /dev/null +++ b/controller/system/config/catalogue.json @@ -0,0 +1 @@ +[{"id":"16793ef0-cdb7-4c80-84eb-9a1a0c02113b","name":"AI","parent":"","sort":0},{"id":"542f3032-2b33-417a-88ee-44dbde7eec68","name":"Cloud Service","parent":"","sort":0},{"id":"6887358d-bcf5-4ebf-b945-6f882c2cde10","name":"Data Analysis","parent":"","sort":0},{"id":"706b75cf-855c-4940-9ce6-05055c95c581","name":"Development","parent":"","sort":0},{"id":"29417527-0245-4db4-8cd8-a9a02deeeda3","name":"IoT","parent":"","sort":0}] \ No newline at end of file diff --git a/controller/system/config/service.json b/controller/system/config/service.json new file mode 100644 index 00000000..e1b75782 --- /dev/null +++ b/controller/system/config/service.json @@ -0,0 +1 @@ +[{"id":"2d8beb9c-121f-4f6c-b860-50c02520e6f3","name":"Data Cruncher API","description":"Provides data cleaning, transformation, and analysis services, supporting both batch processing and real-time analysis.","team":"eef18cfb-140b-4e78-b9eb-ad669d898110","service_type":"public","catalogue":"6887358d-bcf5-4ebf-b945-6f882c2cde10","tags":["Data","Data Analysis"],"logo":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEUAAABFCAYAAAAcjSspAAAAAXNSR0IArs4c6QAABxJJREFUeF7tm2tsFFUUx8+Z3bbQdmYLFWwQ4wuikWpIGiyW3W1J7WyLkoDBQDSaEDUkJhqN8sX4qGhijERDYqIxGPCDLwiG2BDY2wXrTlsCoZIIhERRiYoEDNCdKVva7s6Rabdl291l3rNoOh93zjn3f373zN07995BmL5yCOA0k1wC01DyVMU0lGkoxgaLIlUKYWOLskpF2g6AQj6pRLTD7y95uWtv+V/GUnHOynMo4dbEClJxj/EU6PdUQFh0cCcOGvexZ+kplLAonySAeyxJRl+dFK340ZKvSSfPoIRE+RwAzDWpb5I5AjbHGX/ATgwjvp5ACYsJRoAtRgTp2UhMcF2z6w00tiaWqCoe1kvWxH1ZYkLAhL1pU9ehhFoSlwCxyrSy6zgQcm3d0cp9TsbMjuUqlPq2C0JpuiThhviaAO/fuRPTbsR2FUookogBYbMbwoFgq9QpPOtGbHehiLJWJXknZ04kU6amqmKx2Y5XottQhgGgxAkABWKclZgwz+n4bkNJAYDPadHZ8TgOGn7YJxx0sg23obhdKQCAgxLjy/9LUFwdU8ZBIMDGOBM2OwXG1Upxciarl7CTM11XoaxcSeX9Q8plvYScuE9Ae7tZYIUTsVyFogkMifJFAJjlhFi9GMmygYq+jnlJPTu9+65DCbckFhLiz3pCnLhPgGe6GT/fbizXoYxWS4v8EyDcZ1esEX8VR2p7otUnjNgWsvEEiihSxSAoA3aEmvBNSUywNWH0BIqWUFCUtyLA0yaSs2yKgO/EGf+61QCeQckMumRVqFk/O4Oup1DCkcTjRPiF2QQt2h+SmLDUiq+nUMaqRUkC0EwrYs36+JAWdkUDp8z6eQ6lKZJYkCb8xaxQi/YjEhNKzfp6DkUTGI7IZ4mgxqxYK/aI3BPxaOWXZnyLAqWtjcoG0soVM0Kt2hKA2s0EU8sXRYEyWi1iYgsBvmA1WTN+CPBpnAkbjPoUDUpm7jKEAKafeaPJZduZWeguLpRI/0NIXKeVJC34nJCYUGvEr6hQMn/RfwKQ7Zc4I8mqqv+2nlj5H3q2RYfi5t5QnuQHJCbwNzyUsWqROwDgET2xTtxXEdf2RPkd14tV9ErRxLW3E7e/V3Flty9f8s0NvK+9HdVCYG4IKGN/0fIrBPC+E9WgGwMxKkX51hseSuYx8uwtejjN1RzaX6mdmcm5DFdKQ1v/XX6VW0MESwHhJgAcIqLTHNG38c4Pr54AaC9Yjro9lzEItSmNkKYuo/b27PBvifG3WIISEhMvAaBW1npT5Xga8LFexp+3IzYkyp5VC6np5d2xWTmdULBSwmLyVoKU7n96LgDuXYlVvmoVTFCUjyGAoUmW1Tay/fLtF+WF0tgqP6iq0GujUcsb30FR/hwBnrLRtjlXpDelaGBTtlMOlCZRqU0DHTMXOa/1FYkJpheTQqLcAwANDrRvOMTUasmBEhJlbcA0PABft2WC9VKnsN2wurGJnGdjyjVdGJMYP3FQcVLywYi8CwkeNZOEnq1vmJ/Z1YWG1k6aWi4tTqPvqF5MN+4n1VRVX+YA0CQobvQSAhyPM8HQRlhI1BaeqMyNpA3E/FViwgLNbgJKKKK8AURvGXC2YrJbYsLqQo51dVRSXq1oW6u3WwnulM/42JIFRT4BBPc61UBuHEpy4F8zN1DOxk81Llv2D89VlD5z9aOFD9xr11Tk1RITdl+DIspD4NEqmCmZ3hoflphQnw2lCKO+9gCjCkQjAJQGQgQcPTjo95bFWGsIcC7OhJqiQSGE9wZL+U19HTAIgJM6pK7uSIkwp/bOlDp8CABcPXI+ZdI2FYoyDEC2duuN9C4Crosz/hsjtppN8OH+WTjCaacf7zbqY9UOCc7EO4X5E5USFOWTaPVbHGMqTifL+EV9HWjppFFYVNYS0NfGmrJoRfSR1Bl4fgJKWFTeJqDXLIbTcaPDEgvU2429PKIsShEdtxunkL86Y0jo+W6O4vrkDQDOS0y42alEGpsTS1Sfo5/KjEsblpgwOnGcCkX7ts+RE4bjLc2EZCVjNY6ekAyJylcAtM4p0Jk4myUmbMyBov3g5FQfkV6MRwNbHBY/Gi4kytr7lEOvBDgkMX7GuM7ct+RI4gEg1P4KbV7OHw+fKsipDkwhX30witrR1tEr7xJBsFVpQpW+t0HF0XGkkI6geLkOIX3Ehk4gVFu6o1Wx7BiFlyMtn3/FjyXGP2dHqBnfsNh/BwH3mxmfiYpQufvjscqcBTXdxSTDb89ER1VKr+qJzbawrmslpWs+TU3kT5Uqnxg+fUmwLXmR39DXhyP5WtaFMu7U0HJpMYe+tUi0BJGrBqArBHAKgPYkLwi7CjVgL11z3tpO44Fe5UkCWA+A9QCUGTy1RS7qQ4BtcSZ8phfVMBS9QP+n+9NQ8vTmNJRpKMYe8n8BUo1KZJZZr9MAAAAASUVORK5CYII=","doc":"\u003ch3\u003e\u003cspan style=\"background-color: rgb(241, 196, 15);\"\u003e📌This is just demo service to help you learn the APIPark.\u003c/span\u003e\u003c/h3\u003e\n\u003cp\u003e\u0026nbsp;\u003c/p\u003e\n\u003ch3\u003eDataCruncher Service README\u003c/h3\u003e\n\u003chr\u003e\n\u003ch4\u003e\u003cstrong\u003eOverview\u003c/strong\u003e\u003c/h4\u003e\n\u003cp\u003e\u003cstrong\u003eDataCruncher\u003c/strong\u003e is a powerful service designed for comprehensive data processing. It provides functionalities for data cleaning, transformation, and analysis, enabling you to streamline and optimize your data workflows efficiently.\u003c/p\u003e\n\u003chr\u003e\n\u003ch4\u003e\u003cstrong\u003eService Capabilities\u003c/strong\u003e\u003c/h4\u003e\n\u003col\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eData Cleaning\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eRemove duplicates\u003c/li\u003e\n\u003cli\u003eCorrect formatting issues\u003c/li\u003e\n\u003cli\u003eStandardize values\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eData Transformation\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eNormalize data\u003c/li\u003e\n\u003cli\u003eAggregate and enrich data\u003c/li\u003e\n\u003cli\u003eApply custom transformations\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eData Analysis\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eStatistical summaries\u003c/li\u003e\n\u003cli\u003eData visualizations\u003c/li\u003e\n\u003cli\u003eGenerate insights from data patterns\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr\u003e\n\u003ch4\u003e\u003cstrong\u003eAPI Endpoints\u003c/strong\u003e\u003c/h4\u003e\n\u003col\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eData Cleaning API\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eEndpoint\u003c/strong\u003e: \u003ccode\u003e/api/data/clean\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eMethod\u003c/strong\u003e: \u003ccode\u003ePOST\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eDescription\u003c/strong\u003e: Cleans raw data by removing duplicates and correcting formatting.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eRequest Example\u003c/strong\u003e:\n\u003cdiv class=\"dark bg-gray-950 rounded-md border-[0.5px] border-token-border-medium\"\u003e\n\u003cdiv class=\"flex items-center relative text-token-text-secondary bg-token-main-surface-secondary px-4 py-2 text-xs font-sans justify-between rounded-t-md\"\u003e\u003ccode class=\"!whitespace-pre hljs language-json\"\u003e\u003cspan class=\"hljs-punctuation\"\u003e{\u003c/span\u003e\n \u003cspan class=\"hljs-attr\"\u003e\"data\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e:\u003c/span\u003e \u003cspan class=\"hljs-punctuation\"\u003e[\u003c/span\u003e\n \u003cspan class=\"hljs-punctuation\"\u003e{\u003c/span\u003e\u003cspan class=\"hljs-attr\"\u003e\"name\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e\"John\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e,\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003e\"age\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e\"30\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e,\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003e\"email\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e\"john.doe@example.com\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e}\u003c/span\u003e\n \u003cspan class=\"hljs-punctuation\"\u003e]\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e,\u003c/span\u003e\n \u003cspan class=\"hljs-attr\"\u003e\"rules\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e:\u003c/span\u003e \u003cspan class=\"hljs-punctuation\"\u003e{\u003c/span\u003e\n \u003cspan class=\"hljs-attr\"\u003e\"remove_duplicates\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e:\u003c/span\u003e \u003cspan class=\"hljs-literal\"\u003e\u003cspan class=\"hljs-keyword\"\u003etrue\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e,\u003c/span\u003e\n \u003cspan class=\"hljs-attr\"\u003e\"standardize_format\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e:\u003c/span\u003e \u003cspan class=\"hljs-literal\"\u003e\u003cspan class=\"hljs-keyword\"\u003etrue\u003c/span\u003e\u003c/span\u003e\n \u003cspan class=\"hljs-punctuation\"\u003e}\u003c/span\u003e\n\u003cspan class=\"hljs-punctuation\"\u003e}\u003c/span\u003e\n\u003c/code\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eResponse Example\u003c/strong\u003e:\n\u003cdiv class=\"dark bg-gray-950 rounded-md border-[0.5px] border-token-border-medium\"\u003e\n\u003cdiv class=\"flex items-center relative text-token-text-secondary bg-token-main-surface-secondary px-4 py-2 text-xs font-sans justify-between rounded-t-md\"\u003e\u003ccode class=\"!whitespace-pre hljs language-json\"\u003e\u003cspan class=\"hljs-punctuation\"\u003e{\u003c/span\u003e\n \u003cspan class=\"hljs-attr\"\u003e\"cleaned_data\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e:\u003c/span\u003e \u003cspan class=\"hljs-punctuation\"\u003e[\u003c/span\u003e\n \u003cspan class=\"hljs-punctuation\"\u003e{\u003c/span\u003e\u003cspan class=\"hljs-attr\"\u003e\"name\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e\"John\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e,\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003e\"age\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e:\u003c/span\u003e \u003cspan class=\"hljs-number\"\u003e30\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e,\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003e\"email\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e\"john.doe@example.com\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e}\u003c/span\u003e\n \u003cspan class=\"hljs-punctuation\"\u003e]\u003c/span\u003e\n\u003cspan class=\"hljs-punctuation\"\u003e}\u003c/span\u003e\u0026nbsp;\u003c/code\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eData Transformation API\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eEndpoint\u003c/strong\u003e: \u003ccode\u003e/api/data/transform\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eMethod\u003c/strong\u003e: \u003ccode\u003ePOST\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eDescription\u003c/strong\u003e: Transforms data using specified operations like normalization and aggregation.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eRequest Example\u003c/strong\u003e:\n\u003cdiv class=\"dark bg-gray-950 rounded-md border-[0.5px] border-token-border-medium\"\u003e\n\u003cdiv class=\"overflow-y-auto p-4\" dir=\"ltr\"\u003e\u003ccode class=\"!whitespace-pre hljs language-json\"\u003e\u003cspan class=\"hljs-punctuation\"\u003e{\u003c/span\u003e\n \u003cspan class=\"hljs-attr\"\u003e\"data\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e:\u003c/span\u003e \u003cspan class=\"hljs-punctuation\"\u003e[\u003c/span\u003e\n \u003cspan class=\"hljs-punctuation\"\u003e{\u003c/span\u003e\u003cspan class=\"hljs-attr\"\u003e\"name\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e\"John\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e,\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003e\"age\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e:\u003c/span\u003e \u003cspan class=\"hljs-number\"\u003e30\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e}\u003c/span\u003e\n \u003cspan class=\"hljs-punctuation\"\u003e]\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e,\u003c/span\u003e\n \u003cspan class=\"hljs-attr\"\u003e\"operations\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e:\u003c/span\u003e \u003cspan class=\"hljs-punctuation\"\u003e[\u003c/span\u003e\n \u003cspan class=\"hljs-punctuation\"\u003e{\u003c/span\u003e\u003cspan class=\"hljs-attr\"\u003e\"operation\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e\"normalize\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e,\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003e\"fields\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e:\u003c/span\u003e \u003cspan class=\"hljs-punctuation\"\u003e[\u003c/span\u003e\u003cspan class=\"hljs-string\"\u003e\"age\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e]\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e}\u003c/span\u003e\n \u003cspan class=\"hljs-punctuation\"\u003e]\u003c/span\u003e\n\u003cspan class=\"hljs-punctuation\"\u003e}\u003c/span\u003e\n\u003c/code\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eResponse Example\u003c/strong\u003e:\n\u003cdiv class=\"dark bg-gray-950 rounded-md border-[0.5px] border-token-border-medium\"\u003e\n\u003cdiv class=\"flex items-center relative text-token-text-secondary bg-token-main-surface-secondary px-4 py-2 text-xs font-sans justify-between rounded-t-md\"\u003e\u003ccode class=\"!whitespace-pre hljs language-json\"\u003e\u003cspan class=\"hljs-punctuation\"\u003e{\u003c/span\u003e\n \u003cspan class=\"hljs-attr\"\u003e\"transformed_data\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e:\u003c/span\u003e \u003cspan class=\"hljs-punctuation\"\u003e[\u003c/span\u003e\n \u003cspan class=\"hljs-punctuation\"\u003e{\u003c/span\u003e\u003cspan class=\"hljs-attr\"\u003e\"name\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e\"John\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e,\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003e\"age\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e:\u003c/span\u003e \u003cspan class=\"hljs-number\"\u003e0.75\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e}\u003c/span\u003e\n \u003cspan class=\"hljs-punctuation\"\u003e]\u003c/span\u003e\n\u003cspan class=\"hljs-punctuation\"\u003e}\u003c/span\u003e\n\u003c/code\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eData Analysis API\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eEndpoint\u003c/strong\u003e: \u003ccode\u003e/api/data/analyze\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eMethod\u003c/strong\u003e: \u003ccode\u003ePOST\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eDescription\u003c/strong\u003e: Analyzes data to provide statistical summaries and visualizations.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eRequest Example\u003c/strong\u003e:\n\u003cdiv class=\"dark bg-gray-950 rounded-md border-[0.5px] border-token-border-medium\"\u003e\n\u003cdiv class=\"overflow-y-auto p-4\" dir=\"ltr\"\u003e\u003ccode class=\"!whitespace-pre hljs language-json\"\u003e\u003cspan class=\"hljs-punctuation\"\u003e{\u003c/span\u003e\n \u003cspan class=\"hljs-attr\"\u003e\"data\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e:\u003c/span\u003e \u003cspan class=\"hljs-punctuation\"\u003e[\u003c/span\u003e\n \u003cspan class=\"hljs-punctuation\"\u003e{\u003c/span\u003e\u003cspan class=\"hljs-attr\"\u003e\"name\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e\"John\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e,\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003e\"sales\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e:\u003c/span\u003e \u003cspan class=\"hljs-number\"\u003e200\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e}\u003c/span\u003e\n \u003cspan class=\"hljs-punctuation\"\u003e]\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e,\u003c/span\u003e\n \u003cspan class=\"hljs-attr\"\u003e\"analysis_type\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e\"statistical_summary\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e,\u003c/span\u003e\n \u003cspan class=\"hljs-attr\"\u003e\"parameters\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e:\u003c/span\u003e \u003cspan class=\"hljs-punctuation\"\u003e{\u003c/span\u003e\n \u003cspan class=\"hljs-attr\"\u003e\"include_charts\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e:\u003c/span\u003e \u003cspan class=\"hljs-literal\"\u003e\u003cspan class=\"hljs-keyword\"\u003etrue\u003c/span\u003e\u003c/span\u003e\n \u003cspan class=\"hljs-punctuation\"\u003e}\u003c/span\u003e\n\u003cspan class=\"hljs-punctuation\"\u003e}\u003c/span\u003e\n\u003c/code\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eResponse Example\u003c/strong\u003e:\n\u003cdiv class=\"dark bg-gray-950 rounded-md border-[0.5px] border-token-border-medium\"\u003e\n\u003cdiv class=\"flex items-center relative text-token-text-secondary bg-token-main-surface-secondary px-4 py-2 text-xs font-sans justify-between rounded-t-md\"\u003e\u003ccode class=\"!whitespace-pre hljs language-json\"\u003e\u003cspan class=\"hljs-punctuation\"\u003e{\u003c/span\u003e\n \u003cspan class=\"hljs-attr\"\u003e\"summary\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e:\u003c/span\u003e \u003cspan class=\"hljs-punctuation\"\u003e{\u003c/span\u003e\n \u003cspan class=\"hljs-attr\"\u003e\"mean_sales\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e:\u003c/span\u003e \u003cspan class=\"hljs-number\"\u003e216.67\u003c/span\u003e\n \u003cspan class=\"hljs-punctuation\"\u003e}\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e,\u003c/span\u003e\n \u003cspan class=\"hljs-attr\"\u003e\"charts\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e:\u003c/span\u003e \u003cspan class=\"hljs-punctuation\"\u003e{\u003c/span\u003e\n \u003cspan class=\"hljs-attr\"\u003e\"bar_chart_url\"\u003c/span\u003e\u003cspan class=\"hljs-punctuation\"\u003e:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e\"https://example.com/charts/sales_bar_chart.png\"\u003c/span\u003e\n \u003cspan class=\"hljs-punctuation\"\u003e}\u003c/span\u003e\n\u003cspan class=\"hljs-punctuation\"\u003e}\u003c/span\u003e\n\u003c/code\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr\u003e\n\u003ch4\u003e\u003cstrong\u003eAuthentication\u003c/strong\u003e\u003c/h4\u003e\n\u003cp\u003eAll API requests require an authorization token. Include the token in the \u003ccode\u003eAuthorization\u003c/code\u003e header as \u003ccode\u003eBearer \u0026lt;token\u0026gt;\u003c/code\u003e.\u003c/p\u003e\n\u003chr\u003e\n\u003ch4\u003e\u003cstrong\u003eError Handling\u003c/strong\u003e\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e400 Bad Request\u003c/strong\u003e: Invalid input data or parameters.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e401 Unauthorized\u003c/strong\u003e: Authentication token is missing or invalid.\u003c/li\u003e\n\u003c/ul\u003e"},{"id":"bb4f4ef3-f9f0-4383-9a5b-9f3579850077","name":"AI Model Trainer","description":"Facilitates training and optimization of machine learning models, supporting various algorithms and hyperparameter tuning.","team":"eef18cfb-140b-4e78-b9eb-ad669d898110","service_type":"public","catalogue":"16793ef0-cdb7-4c80-84eb-9a1a0c02113b","tags":null,"logo":"","doc":""},{"id":"f4362f31-a0e0-4c26-84cc-6b28b11ebab7","name":"IoT Device Manager","description":"Manages and monitors IoT devices, offering status updates, configuration changes, and data stream monitoring.","team":"eef18cfb-140b-4e78-b9eb-ad669d898110","service_type":"public","catalogue":"29417527-0245-4db4-8cd8-a9a02deeeda3","tags":null,"logo":"","doc":""}] \ No newline at end of file diff --git a/controller/system/config/subscribe.json b/controller/system/config/subscribe.json new file mode 100644 index 00000000..62d7c05d --- /dev/null +++ b/controller/system/config/subscribe.json @@ -0,0 +1 @@ +[{"id":"13a1d172-6e67-4581-a95b-61ea4c54ebbb","service":"2d8beb9c-121f-4f6c-b860-50c02520e6f3","subscriber":"358caa9f-a079-44dd-8b28-c1d065777b6c","team":"","applier":"","from":1}] \ No newline at end of file diff --git a/controller/system/config/team.json b/controller/system/config/team.json new file mode 100644 index 00000000..bf6534a5 --- /dev/null +++ b/controller/system/config/team.json @@ -0,0 +1 @@ +[{"id":"eef18cfb-140b-4e78-b9eb-ad669d898110","name":"Demo Team","description":""}] \ No newline at end of file diff --git a/controller/system/config/upstream.json b/controller/system/config/upstream.json new file mode 100644 index 00000000..cd074a93 --- /dev/null +++ b/controller/system/config/upstream.json @@ -0,0 +1 @@ +[{"id":"2d8beb9c-121f-4f6c-b860-50c02520e6f3","name":"","service":"2d8beb9c-121f-4f6c-b860-50c02520e6f3","driver":"static","balance":"round-robin","timeout":3,"retry":3,"remark":"","limit_peer_second":10000,"proxy_headers":[],"scheme":"HTTPS","pass_host":"pass","upstream_host":"","nodes":[{"address":"demoapi.apipark.com","weight":999}],"discover":{"discover":"","service":""}}] \ No newline at end of file diff --git a/controller/system/iml.go b/controller/system/iml.go new file mode 100644 index 00000000..c471a6c4 --- /dev/null +++ b/controller/system/iml.go @@ -0,0 +1,167 @@ +package system + +import ( + "archive/zip" + "bytes" + "encoding/json" + "fmt" + application_authorization "github.com/APIParkLab/APIPark/module/application-authorization" + "github.com/APIParkLab/APIPark/module/catalogue" + "github.com/APIParkLab/APIPark/module/router" + "github.com/APIParkLab/APIPark/module/service" + "github.com/APIParkLab/APIPark/module/subscribe" + "github.com/APIParkLab/APIPark/module/team" + "github.com/APIParkLab/APIPark/module/upstream" + "github.com/gin-gonic/gin" + "net/http" + "time" +) + +var _ IExportConfigController = (*imlExportConfigController)(nil) + +type imlExportConfigController struct { + teamModule team.ITeamExportModule `autowired:""` + serviceModule service.IExportServiceModule `autowired:""` + appModule service.IExportAppModule `autowired:""` + routerModule router.IExportRouterModule `autowired:""` + upstreamModule upstream.IExportUpstreamModule `autowired:""` + applicationAuthorizationModule application_authorization.IExportAuthorizationModule `autowired:""` + catalogueModule catalogue.IExportCatalogueModule `autowired:""` + subscribeModule subscribe.IExportSubscribeModule `autowired:""` + applyModule subscribe.IExportSubscribeApprovalModule `autowired:""` +} + +type ExportFile struct { + Driver string `json:"driver"` + Data interface{} `json:"data"` +} + +func (e *ExportFile) Byte() []byte { + b, _ := json.Marshal(e.Data) + return b +} + +func zipFile(files []*ExportFile) (*bytes.Buffer, error) { + // 创建一个缓冲区用于存储 ZIP 文件内容 + buf := new(bytes.Buffer) + zipWriter := zip.NewWriter(buf) + now := time.Now() + // 将文件写入 ZIP + for _, file := range files { + header := &zip.FileHeader{ + Name: fmt.Sprintf("%s.json", file.Driver), + Method: zip.Deflate, + Modified: now, + } + f, err := zipWriter.CreateHeader(header) + if err != nil { + return nil, fmt.Errorf("failed to create zip file: %v", err) + } + _, err = f.Write(file.Byte()) + if err != nil { + return nil, fmt.Errorf("failed to write to zip file: %v", err) + } + } + + // 关闭 ZIP writer,完成压缩过程 + err := zipWriter.Close() + if err != nil { + return nil, fmt.Errorf("failed to close zip writer: %v", err) + } + return buf, nil +} + +func (i *imlExportConfigController) ExportAll(ctx *gin.Context) error { + files, err := i.appendFiles(ctx) + if err != nil { + return err + } + buf, err := zipFile(files) + if err != nil { + + } + + ctx.DataFromReader(http.StatusOK, int64(buf.Len()), "application/zip", buf, map[string]string{ + "Content-Disposition": "attachment; filename=\"export.zip\"", + }) + return nil +} + +func (i *imlExportConfigController) appendFiles(ctx *gin.Context) ([]*ExportFile, error) { + type exportConfig struct { + exportFunc func(ctx *gin.Context) (interface{}, error) + driver string + } + + exports := []exportConfig{ + { + exportFunc: func(ctx *gin.Context) (interface{}, error) { + return i.teamModule.ExportAll(ctx) + }, + driver: "team", + }, + { + exportFunc: func(ctx *gin.Context) (interface{}, error) { + return i.serviceModule.ExportAll(ctx) + }, + driver: "service", + }, + { + exportFunc: func(ctx *gin.Context) (interface{}, error) { + return i.appModule.ExportAll(ctx) + }, + driver: "app", + }, + { + exportFunc: func(ctx *gin.Context) (interface{}, error) { + return i.routerModule.ExportAll(ctx) + }, + driver: "api", + }, + { + exportFunc: func(ctx *gin.Context) (interface{}, error) { + return i.upstreamModule.ExportAll(ctx) + }, + driver: "upstream", + }, + { + exportFunc: func(ctx *gin.Context) (interface{}, error) { + return i.applicationAuthorizationModule.ExportAll(ctx) + }, + driver: "authorization", + }, + { + exportFunc: func(ctx *gin.Context) (interface{}, error) { + return i.catalogueModule.ExportAll(ctx) + }, + driver: "catalogue", + }, + { + exportFunc: func(ctx *gin.Context) (interface{}, error) { + return i.subscribeModule.ExportAll(ctx) + }, + driver: "subscribe", + }, + { + exportFunc: func(ctx *gin.Context) (interface{}, error) { + return i.applyModule.ExportAll(ctx) + }, + driver: "apply", + }, + } + + files := make([]*ExportFile, 0, len(exports)) + for _, config := range exports { + data, err := config.exportFunc(ctx) + if err != nil { + return nil, fmt.Errorf("[%s] failed to export data: %v", config.driver, err) + } + + files = append(files, &ExportFile{ + Driver: config.driver, + Data: data, + }) + } + + return files, nil +} diff --git a/controller/system/import.go b/controller/system/import.go new file mode 100644 index 00000000..cfcc23a6 --- /dev/null +++ b/controller/system/import.go @@ -0,0 +1,454 @@ +package system + +import ( + "context" + "embed" + "encoding/json" + "errors" + "fmt" + application_authorization "github.com/APIParkLab/APIPark/module/application-authorization" + application_authorization_dto "github.com/APIParkLab/APIPark/module/application-authorization/dto" + "github.com/APIParkLab/APIPark/module/catalogue" + catalogue_dto "github.com/APIParkLab/APIPark/module/catalogue/dto" + "github.com/APIParkLab/APIPark/module/publish" + "github.com/APIParkLab/APIPark/module/publish/dto" + "github.com/APIParkLab/APIPark/module/release" + dto2 "github.com/APIParkLab/APIPark/module/release/dto" + "github.com/APIParkLab/APIPark/module/router" + router_dto "github.com/APIParkLab/APIPark/module/router/dto" + "github.com/APIParkLab/APIPark/module/service" + service_dto "github.com/APIParkLab/APIPark/module/service/dto" + "github.com/APIParkLab/APIPark/module/subscribe" + subscribe_dto "github.com/APIParkLab/APIPark/module/subscribe/dto" + "github.com/APIParkLab/APIPark/module/team" + team_dto "github.com/APIParkLab/APIPark/module/team/dto" + "github.com/APIParkLab/APIPark/module/upstream" + upstream_dto "github.com/APIParkLab/APIPark/module/upstream/dto" + "github.com/eolinker/go-common/store" + "github.com/gin-gonic/gin" + "gorm.io/gorm" +) + +var ( + //go:embed config/*.json + importConfigs embed.FS +) + +func unmarshal[T any](name string) ([]*T, error) { + data, err := importConfigs.ReadFile(fmt.Sprintf("config/%s.json", name)) + if err != nil { + return nil, fmt.Errorf("fail to read file(%s): %v", name, err) + } + t := make([]*T, 0) + err = json.Unmarshal(data, &t) + return t, err +} + +var ( + _ IImportConfigController = (*imlImportConfigController)(nil) +) + +type imlImportConfigController struct { + teamModule team.ITeamModule `autowired:""` + serviceModule service.IServiceModule `autowired:""` + appModule service.IAppModule `autowired:""` + apiModule router.IRouterModule `autowired:""` + upstreamModule upstream.IUpstreamModule `autowired:""` + applicationAuthorizationModule application_authorization.IAuthorizationModule `autowired:""` + catalogueModule catalogue.ICatalogueModule `autowired:""` + subscribeModule subscribe.ISubscribeModule `autowired:""` + applyModule subscribe.ISubscribeApprovalModule `autowired:""` + publishModule publish.IPublishModule `autowired:""` + releaseModule release.IReleaseModule `autowired:""` + transaction store.ITransaction `autowired:""` +} + +func (i *imlImportConfigController) ImportAll(ctx *gin.Context) error { + return i.transaction.Transaction(ctx, func(transCtx context.Context) error { + err := i.importTeams(transCtx) + if err != nil { + return err + } + + err = i.importCatalogues(transCtx) + if err != nil { + return err + } + + err = i.importServices(transCtx) + if err != nil { + return err + } + + err = i.importApplications(transCtx) + if err != nil { + return err + } + + err = i.importApplicationAuth(transCtx) + if err != nil { + return err + } + + err = i.importApis(transCtx) + if err != nil { + return err + } + + err = i.importUpstreams(transCtx) + if err != nil { + return err + } + + err = i.publish(transCtx) + if err != nil { + return err + } + + err = i.importSubscribers(transCtx) + if err != nil { + return err + } + + return nil + }) + +} + +func (i *imlImportConfigController) importTeams(ctx context.Context) error { + data, err := unmarshal[team_dto.ExportTeam]("team") + if err != nil { + return err + } + for _, d := range data { + // 判断是否存在,如果存在,则更新 + _, err = i.teamModule.GetTeam(ctx, d.Id) + if err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + return err + } + _, err = i.teamModule.Create(ctx, &team_dto.CreateTeam{ + Id: d.Id, + Name: d.Name, + Description: d.Description, + }) + if err != nil { + return fmt.Errorf("create team(%s) error: %v", d.Id, err) + } + continue + } + _, err = i.teamModule.Edit(ctx, d.Id, &team_dto.EditTeam{ + Name: &d.Name, + Description: &d.Description, + }) + if err != nil { + return fmt.Errorf("update team(%s) error: %v", d.Id, err) + } + } + return nil +} + +func (i *imlImportConfigController) importServices(ctx context.Context) error { + data, err := unmarshal[service_dto.ExportService]("service") + if err != nil { + return err + } + for _, d := range data { + // 判断是否存在,如果存在,则更新 + _, err = i.serviceModule.Get(ctx, d.Id) + if err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + return err + } + _, err = i.serviceModule.Create(ctx, d.Team, &service_dto.CreateService{ + Id: d.Id, + Name: d.Name, + Prefix: d.Prefix, + Description: d.Description, + ServiceType: d.ServiceType, + Logo: d.Logo, + Tags: d.Tags, + Catalogue: d.Catalogue, + }) + if err != nil { + return fmt.Errorf("create service(%s) error: %v", d.Id, err) + } + } else { + _, err = i.serviceModule.Edit(ctx, d.Id, &service_dto.EditService{ + Name: &d.Name, + Description: &d.Description, + ServiceType: &d.ServiceType, + Catalogue: &d.Catalogue, + Logo: &d.Logo, + Tags: &d.Tags, + }) + if err != nil { + return fmt.Errorf("update service(%s) error: %v", d.Id, err) + } + } + + err = i.serviceModule.SaveServiceDoc(ctx, d.Id, &service_dto.SaveServiceDoc{Doc: d.Doc}) + if err != nil { + return fmt.Errorf("save service(%s) doc error: %v", d.Id, err) + } + } + return nil +} + +func (i *imlImportConfigController) importApplications(ctx context.Context) error { + data, err := unmarshal[service_dto.ExportApp]("app") + if err != nil { + return err + } + for _, d := range data { + // 判断是否存在,如果存在,则更新 + _, err = i.appModule.GetApp(ctx, d.Id) + if err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + return err + } + _, err = i.appModule.CreateApp(ctx, d.Team, &service_dto.CreateApp{ + Id: d.Id, + Name: d.Name, + Description: d.Description, + }) + if err != nil { + return fmt.Errorf("create app(%s) error: %v", d.Id, err) + } + + continue + } + _, err = i.appModule.UpdateApp(ctx, d.Id, &service_dto.UpdateApp{ + Name: &d.Name, + Description: &d.Description, + }) + if err != nil { + return fmt.Errorf("update app(%s) error: %v", d.Id, err) + } + + } + return nil +} + +func (i *imlImportConfigController) importApis(ctx context.Context) error { + data, err := unmarshal[router_dto.Export]("api") + if err != nil { + return err + } + for _, d := range data { + var proxy *router_dto.InputProxy + if d.Proxy != nil { + proxy = &router_dto.InputProxy{ + Path: d.Proxy.Path, + Timeout: d.Proxy.Timeout, + Retry: d.Proxy.Retry, + Headers: d.Proxy.Headers, + Extends: d.Proxy.Extends, + Plugins: d.Proxy.Plugins, + } + } + // 判断是否存在,如果存在,则更新 + _, err = i.apiModule.Detail(ctx, d.Service, d.Id) + if err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + return err + } + _, err = i.apiModule.Create(ctx, d.Service, &router_dto.Create{ + Id: d.Id, + Path: d.Path, + Methods: d.Method, + Description: d.Description, + MatchRules: d.MatchRules, + Proxy: proxy, + }) + if err != nil { + return fmt.Errorf("create api(%s) error: %v", d.Id, err) + } + + continue + } + info := &router_dto.Edit{ + + Proxy: proxy, + //Doc: &d.Doc, + } + + _, err = i.apiModule.Edit(ctx, d.Service, d.Id, info) + if err != nil { + return fmt.Errorf("update api(%s) error: %v", d.Id, err) + } + + } + return nil +} + +func (i *imlImportConfigController) importCatalogues(ctx context.Context) error { + data, err := unmarshal[catalogue_dto.ExportCatalogue]("catalogue") + if err != nil { + return err + } + for _, d := range data { + _, err = i.catalogueModule.Get(ctx, d.Id) + if err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + return err + } + err = i.catalogueModule.Create(ctx, &catalogue_dto.CreateCatalogue{ + Id: d.Id, + Name: d.Name, + Parent: &d.Parent, + Sort: &d.Sort, + }) + if err != nil { + return fmt.Errorf("create catalogue(%s) error: %v", d.Id, err) + } + continue + } + err = i.catalogueModule.Edit(ctx, d.Id, &catalogue_dto.EditCatalogue{ + Name: &d.Name, + Parent: &d.Parent, + Sort: &d.Sort, + }) + if err != nil { + return fmt.Errorf("update catalogue(%s) error: %v", d.Id, err) + } + + } + return nil +} + +func (i *imlImportConfigController) importUpstreams(ctx context.Context) error { + data, err := unmarshal[upstream_dto.ExportUpstream]("upstream") + if err != nil { + return err + } + for _, d := range data { + _, err = i.upstreamModule.Save(ctx, d.Service, d.Upstream) + if err != nil { + return fmt.Errorf("update upstream(%s) error: %v", d.Service, err) + } + } + return nil +} + +func (i *imlImportConfigController) importApplicationAuth(ctx context.Context) error { + data, err := unmarshal[application_authorization_dto.ExportAuthorization]("authorization") + if err != nil { + return err + } + for _, d := range data { + _, err := i.applicationAuthorizationModule.Info(ctx, d.Application, d.UUID) + if err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + return err + } + _, err = i.applicationAuthorizationModule.AddAuthorization(ctx, d.Application, &application_authorization_dto.CreateAuthorization{ + UUID: d.UUID, + Name: d.Name, + Driver: d.Driver, + Position: d.Position, + TokenName: d.TokenName, + ExpireTime: d.ExpireTime, + Config: d.Config, + HideCredential: d.HideCredential, + }) + if err != nil { + return fmt.Errorf("create authorization(%s) error: %v", d.UUID, err) + } + + continue + } + _, err = i.applicationAuthorizationModule.EditAuthorization(ctx, d.Application, d.UUID, &application_authorization_dto.EditAuthorization{ + Name: &d.Name, + Position: &d.Position, + TokenName: &d.TokenName, + ExpireTime: &d.ExpireTime, + Config: &d.Config, + HideCredential: &d.HideCredential, + }) + if err != nil { + return fmt.Errorf("update authorization(%s) error: %v", d.UUID, err) + } + + } + return nil +} + +func (i *imlImportConfigController) publish(ctx context.Context) error { + data, err := unmarshal[service_dto.ExportService]("service") + if err != nil { + return err + } + for _, d := range data { + serviceId := d.Id + newReleaseId, err := i.releaseModule.Create(ctx, serviceId, &dto2.CreateInput{ + Version: "v1", + Remark: "demo release", + }) + if err != nil { + continue + } + apply, err := i.publishModule.Apply(ctx, serviceId, &dto.ApplyInput{ + Release: newReleaseId, + Remark: "发布申请", + }) + if err != nil { + return err + } + err = i.publishModule.Accept(ctx, serviceId, apply.Id, "") + if err != nil { + i.releaseModule.Delete(ctx, serviceId, newReleaseId) + return err + } + err = i.publishModule.Publish(ctx, serviceId, apply.Id) + if err != nil { + i.releaseModule.Delete(ctx, serviceId, newReleaseId) + return err + } + err = i.publishModule.Publish(ctx, serviceId, apply.Id) + if err != nil { + i.releaseModule.Delete(ctx, serviceId, newReleaseId) + return err + } + } + return nil +} + +func (i *imlImportConfigController) importSubscribers(ctx context.Context) error { + + applyData, err := unmarshal[subscribe_dto.ExportApproval]("apply") + if err != nil { + return err + } + for _, d := range applyData { + err = i.catalogueModule.Subscribe(ctx, &catalogue_dto.SubscribeService{ + Service: d.Service, + Applications: []string{ + d.Application, + }, + Reason: d.Reason, + }) + if err != nil { + return fmt.Errorf("application(%s) subscribe service(%s) error: %v", d.Application, d.Service, err) + } + } + data, err := unmarshal[subscribe_dto.ExportSubscriber]("subscribe") + if err != nil { + return err + } + for _, d := range data { + err = i.subscribeModule.ExistSubscriber(ctx, d.Service, d.Subscriber) + if err == nil { + continue + } + err = i.subscribeModule.AddSubscriber(ctx, d.Service, &subscribe_dto.AddSubscriber{ + Application: d.Subscriber, + }) + if err != nil { + return fmt.Errorf("update subscriber(%s) error: %v", d.Id, err) + } + + } + + return nil +} diff --git a/controller/system/system.go b/controller/system/system.go new file mode 100644 index 00000000..49cc93c3 --- /dev/null +++ b/controller/system/system.go @@ -0,0 +1,25 @@ +package system + +import ( + "github.com/eolinker/go-common/autowire" + "github.com/gin-gonic/gin" + "reflect" +) + +type IExportConfigController interface { + ExportAll(ctx *gin.Context) error +} + +type IImportConfigController interface { + ImportAll(ctx *gin.Context) error +} + +func init() { + autowire.Auto[IExportConfigController](func() reflect.Value { + return reflect.ValueOf(new(imlExportConfigController)) + }) + + autowire.Auto[IImportConfigController](func() reflect.Value { + return reflect.ValueOf(new(imlImportConfigController)) + }) +} diff --git a/controller/upstream/iml.go b/controller/upstream/iml.go index ac16a361..0bce956c 100644 --- a/controller/upstream/iml.go +++ b/controller/upstream/iml.go @@ -22,6 +22,6 @@ func (i *imlUpstreamController) Get(ctx *gin.Context, serviceId string) (upstrea return i.upstreamModule.Get(ctx, serviceId) } -func (i *imlUpstreamController) Save(ctx *gin.Context, serviceId string, upstream *upstream_dto.UpstreamConfig) (upstream_dto.UpstreamConfig, error) { - return i.upstreamModule.Save(ctx, serviceId, *upstream) +func (i *imlUpstreamController) Save(ctx *gin.Context, serviceId string, upstream *upstream_dto.Upstream) (upstream_dto.UpstreamConfig, error) { + return i.upstreamModule.Save(ctx, serviceId, upstream) } diff --git a/controller/upstream/upstream.go b/controller/upstream/upstream.go index 10ce9698..9db799dd 100644 --- a/controller/upstream/upstream.go +++ b/controller/upstream/upstream.go @@ -2,16 +2,16 @@ package upstream import ( "reflect" - + "github.com/eolinker/go-common/autowire" - + upstream_dto "github.com/APIParkLab/APIPark/module/upstream/dto" "github.com/gin-gonic/gin" ) type IUpstreamController interface { Get(ctx *gin.Context, serviceId string) (upstream_dto.UpstreamConfig, error) - Save(ctx *gin.Context, serviceId string, upstream *upstream_dto.UpstreamConfig) (upstream_dto.UpstreamConfig, error) + Save(ctx *gin.Context, serviceId string, upstream *upstream_dto.Upstream) (upstream_dto.UpstreamConfig, error) } func init() { diff --git a/frontend/frontend.go b/frontend/frontend.go index 96f8a084..d7d91afc 100644 --- a/frontend/frontend.go +++ b/frontend/frontend.go @@ -49,9 +49,7 @@ func getFileSystem(dir string) http.FileSystem { if err != nil { panic(err) } - return http.FS(fDir) - } type Frontend struct { @@ -83,10 +81,10 @@ func IndexHtml(ginCtx *gin.Context) { func (f *Frontend) Api() []pm3.Api { return []pm3.Api{ - pm3.CreateApiSimple(http.MethodGet, "/favicon.ico", func(ginCtx *gin.Context) { + pm3.CreateRouterSimple(http.MethodGet, "/favicon.ico", func(ginCtx *gin.Context) { ginCtx.Data(http.StatusOK, iconType, iconContent) }), - pm3.CreateApiSimple(http.MethodGet, "/vite.svg", func(ginCtx *gin.Context) { + pm3.CreateRouterSimple(http.MethodGet, "/vite.svg", func(ginCtx *gin.Context) { ginCtx.Data(http.StatusOK, viteContentType, viteContent) }), } diff --git a/gateway/admin/http.go b/gateway/admin/http.go index 08296ac0..8f530396 100644 --- a/gateway/admin/http.go +++ b/gateway/admin/http.go @@ -9,10 +9,13 @@ import ( "io" "net/http" "strings" + "time" ) var ( - httpClient = &http.Client{} + httpClient = &http.Client{ + Timeout: 5 * time.Second, + } ErrorInvalidAdminAddress = errors.New("invalid address") ) diff --git a/go.mod b/go.mod index 3fd364aa..56f85aca 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,10 @@ require ( github.com/eolinker/eosc v0.17.3 github.com/eolinker/go-common v1.0.4 github.com/gabriel-vasile/mimetype v1.4.4 + github.com/getkin/kin-openapi v0.127.0 github.com/gin-gonic/gin v1.10.0 + github.com/go-openapi/loads v0.22.0 + github.com/go-openapi/validate v0.24.0 github.com/google/uuid v1.6.0 github.com/influxdata/influxdb-client-go/v2 v2.14.0 github.com/urfave/cli/v2 v2.27.2 @@ -20,6 +23,7 @@ require ( require ( github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect + github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/bytedance/sonic v1.11.6 // indirect github.com/bytedance/sonic/loader v0.1.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect @@ -31,6 +35,13 @@ require ( github.com/ghodss/yaml v1.0.0 // indirect github.com/gin-contrib/gzip v1.0.1 // indirect github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-openapi/analysis v0.23.0 // indirect + github.com/go-openapi/errors v0.22.0 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect + github.com/go-openapi/spec v0.21.0 // indirect + github.com/go-openapi/strfmt v0.23.0 // indirect + github.com/go-openapi/swag v0.23.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.20.0 // indirect @@ -39,17 +50,23 @@ require ( github.com/google/go-cmp v0.6.0 // indirect github.com/gorilla/websocket v1.4.2 // indirect github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect + github.com/invopop/yaml v0.3.1 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect + github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.7 // indirect - github.com/kr/pretty v0.1.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/oapi-codegen/runtime v1.0.0 // indirect + github.com/oklog/ulid v1.3.1 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/redis/go-redis/v9 v9.5.3 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect @@ -57,6 +74,7 @@ require ( github.com/ugorji/go/codec v1.2.12 // indirect github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.13 // indirect + go.mongodb.org/mongo-driver v1.14.0 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.23.0 // indirect @@ -65,7 +83,6 @@ require ( golang.org/x/sys v0.22.0 // indirect golang.org/x/text v0.16.0 // indirect google.golang.org/protobuf v1.34.1 // indirect - gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gorm.io/driver/mysql v1.5.2 // indirect ) diff --git a/go.sum b/go.sum index 5664eb36..f105dc6a 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,8 @@ github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= @@ -35,6 +37,8 @@ github.com/eolinker/go-common v1.0.4 h1:F0akjnzJfIFOVmK30fD0SsCLU7DAKPXuY21MeyMm github.com/eolinker/go-common v1.0.4/go.mod h1:Kb/jENMN1mApnodvRgV4YwO9FJby1Jkt2EUjrBjvSX4= github.com/gabriel-vasile/mimetype v1.4.4 h1:QjV6pZ7/XZ7ryI2KuyeEDE8wnh7fHP9YnQy+R0LnH8I= github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSeEuJfJE+TtfvdB/s= +github.com/getkin/kin-openapi v0.127.0 h1:Mghqi3Dhryf3F8vR370nN67pAERW+3a95vomb3MAREY= +github.com/getkin/kin-openapi v0.127.0/go.mod h1:OZrfXzUfGrNbsKj+xmFBx6E5c6yH3At/tAKSc2UszXM= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/gzip v1.0.1 h1:HQ8ENHODeLY7a4g1Au/46Z92bdGFl74OhxcZble9WJE= @@ -43,6 +47,24 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU= +github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo= +github.com/go-openapi/errors v0.22.0 h1:c4xY/OLxUBSTiepAg3j/MHuAv5mJhnf53LLMWFB+u/w= +github.com/go-openapi/errors v0.22.0/go.mod h1:J3DmZScxCDufmIMsdOuDHxJbdOGC0xtUynjIx092vXE= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/loads v0.22.0 h1:ECPGd4jX1U6NApCGG1We+uEozOAvXvJSF4nnwHZ8Aco= +github.com/go-openapi/loads v0.22.0/go.mod h1:yLsaTCS92mnSAZX5WWoxszLj0u+Ojl+Zs5Stn1oF+rs= +github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY= +github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= +github.com/go-openapi/strfmt v0.23.0 h1:nlUS6BCqcnAk0pyhi9Y+kdDVZdZMHfEKQiS4HaMgO/c= +github.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58= +github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= @@ -53,6 +75,8 @@ github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBEx github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= +github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= @@ -66,10 +90,14 @@ github.com/influxdata/influxdb-client-go/v2 v2.14.0 h1:AjbBfJuq+QoaXNcrova8smSjw github.com/influxdata/influxdb-client-go/v2 v2.14.0/go.mod h1:Ahpm3QXKMJslpXl3IftVLVezreAUtBOTZssDrjZEFHI= github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 h1:W9WBk7wlPfJLvMCdtV4zPulc4uCPrlywQOmbFOhgQNU= github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= +github.com/invopop/yaml v0.3.1 h1:f0+ZpmhfBSS4MhG+4HYseMdJhoeeopbSKbq5Rpeelso= +github.com/invopop/yaml v0.3.1/go.mod h1:PMOp3nn4/12yEZUFfmOuNHJsZToEEOwoWsT+D81KkeA= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= @@ -77,30 +105,41 @@ github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02 github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/oapi-codegen/runtime v1.0.0 h1:P4rqFX5fMFWqRzY9M/3YF9+aPSPPB06IzP2P7oOxrWo= github.com/oapi-codegen/runtime v1.0.0/go.mod h1:LmCUMQuPB4M/nLXilQXhHw+BLZdDb18B34OO356yJ/A= +github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= +github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/redis/go-redis/v9 v9.5.3 h1:fOAp1/uJG+ZtcITgZOfYFmTKPE7n4Vclj1wZFgRciUU= github.com/redis/go-redis/v9 v9.5.3/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= @@ -126,6 +165,8 @@ github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 h1:+qGGcbkzsfDQNPPe github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913/go.mod h1:4aEEwZQutDLsQv2Deui4iYQ6DWTxR14g6m8Wv88+Xqk= go.etcd.io/etcd/client/pkg/v3 v3.5.13 h1:RVZSAnWWWiI5IrYAXjQorajncORbS0zI48LQlE2kQWg= go.etcd.io/etcd/client/pkg/v3 v3.5.13/go.mod h1:XxHT4u1qU12E2+po+UVPrEeL94Um6zL58ppuJWXSAB8= +go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80= +go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= @@ -150,8 +191,8 @@ golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 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= diff --git a/module/api-doc/api_doc.go b/module/api-doc/api_doc.go new file mode 100644 index 00000000..69573516 --- /dev/null +++ b/module/api-doc/api_doc.go @@ -0,0 +1,58 @@ +package api_doc + +import ( + "context" + "errors" + api_doc_dto "github.com/APIParkLab/APIPark/module/api-doc/dto" + api_doc "github.com/APIParkLab/APIPark/service/api-doc" + "github.com/APIParkLab/APIPark/service/service" + + "github.com/eolinker/go-common/auto" + "github.com/google/uuid" + "gorm.io/gorm" +) + +var _ IAPIDocModule = (*imlAPIDocModule)(nil) + +type imlAPIDocModule struct { + apiDocService api_doc.IAPIDocService `autowired:""` + serviceService service.IServiceService `autowired:""` +} + +func (i *imlAPIDocModule) UpdateDoc(ctx context.Context, serviceId string, input *api_doc_dto.UpdateDoc) (*api_doc_dto.ApiDocDetail, error) { + _, err := i.serviceService.Get(ctx, serviceId) + if err != nil { + return nil, err + } + if input.Id == "" { + input.Id = uuid.New().String() + } + err = i.apiDocService.UpdateDoc(ctx, serviceId, &api_doc.UpdateDoc{ + ID: input.Id, + Service: serviceId, + Content: input.Content, + }) + if err != nil { + return nil, err + } + return i.GetDoc(ctx, serviceId) +} + +func (i *imlAPIDocModule) GetDoc(ctx context.Context, serviceId string) (*api_doc_dto.ApiDocDetail, error) { + _, err := i.serviceService.Get(ctx, serviceId) + if err != nil { + return nil, err + } + info, err := i.apiDocService.GetDoc(ctx, serviceId) + if err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + return nil, err + } + return nil, nil + } + return &api_doc_dto.ApiDocDetail{ + Content: info.Content, + Updater: info.Updater, + UpdateTime: auto.TimeLabel(info.UpdateAt), + }, nil +} diff --git a/module/api-doc/dto/input.go b/module/api-doc/dto/input.go new file mode 100644 index 00000000..1d5c5772 --- /dev/null +++ b/module/api-doc/dto/input.go @@ -0,0 +1,6 @@ +package api_doc_dto + +type UpdateDoc struct { + Id string `json:"id"` + Content string `json:"content"` +} diff --git a/module/api-doc/dto/output.go b/module/api-doc/dto/output.go new file mode 100644 index 00000000..1f420ebb --- /dev/null +++ b/module/api-doc/dto/output.go @@ -0,0 +1,9 @@ +package api_doc_dto + +import "github.com/eolinker/go-common/auto" + +type ApiDocDetail struct { + Content string `json:"content"` + Updater string `json:"updater"` + UpdateTime auto.TimeLabel `json:"update_time"` +} diff --git a/module/api-doc/iml.go b/module/api-doc/iml.go new file mode 100644 index 00000000..6c4e4d63 --- /dev/null +++ b/module/api-doc/iml.go @@ -0,0 +1,20 @@ +package api_doc + +import ( + "context" + api_doc_dto "github.com/APIParkLab/APIPark/module/api-doc/dto" + "github.com/eolinker/go-common/autowire" + "reflect" +) + +type IAPIDocModule interface { + UpdateDoc(ctx context.Context, serviceId string, input *api_doc_dto.UpdateDoc) (*api_doc_dto.ApiDocDetail, error) + GetDoc(ctx context.Context, serviceId string) (*api_doc_dto.ApiDocDetail, error) +} + +func init() { + apiDocModule := new(imlAPIDocModule) + autowire.Auto[IAPIDocModule](func() reflect.Value { + return reflect.ValueOf(apiDocModule) + }) +} diff --git a/module/api/api.go b/module/api/api.go deleted file mode 100644 index b66d8fe6..00000000 --- a/module/api/api.go +++ /dev/null @@ -1,42 +0,0 @@ -package api - -import ( - "context" - "reflect" - - "github.com/eolinker/go-common/autowire" - - api_dto "github.com/APIParkLab/APIPark/module/api/dto" -) - -type IApiModule interface { - // Detail 获取API详情 - Detail(ctx context.Context, serviceId string, apiId string) (*api_dto.ApiDetail, error) - // SimpleDetail 获取API简要详情 - SimpleDetail(ctx context.Context, serviceId string, apiId string) (*api_dto.ApiSimpleDetail, error) - // Search 获取API列表 - Search(ctx context.Context, keyword string, serviceId string) ([]*api_dto.ApiItem, error) - // SimpleSearch 获取API简要列表 - SimpleSearch(ctx context.Context, keyword string, serviceId string) ([]*api_dto.ApiSimpleItem, error) - SimpleList(ctx context.Context, serviceId string) ([]*api_dto.ApiSimpleItem, error) - // Create 创建API - Create(ctx context.Context, serviceId string, dto *api_dto.CreateApi) (*api_dto.ApiSimpleDetail, error) - // Edit 编辑API - Edit(ctx context.Context, serviceId string, apiId string, dto *api_dto.EditApi) (*api_dto.ApiSimpleDetail, error) - // Delete 删除API - Delete(ctx context.Context, serviceId string, apiId string) error - // Copy 复制API - Copy(ctx context.Context, serviceId string, apiId string, dto *api_dto.CreateApi) (*api_dto.ApiSimpleDetail, error) - // ApiDocDetail 获取API文档详情 - ApiDocDetail(ctx context.Context, serviceId string, apiId string) (*api_dto.ApiDocDetail, error) - // ApiProxyDetail 获取API代理详情 - ApiProxyDetail(ctx context.Context, serviceId string, apiId string) (*api_dto.ApiProxyDetail, error) - // Prefix 获取API前缀 - Prefix(ctx context.Context, serviceId string) (string, error) -} - -func init() { - autowire.Auto[IApiModule](func() reflect.Value { - return reflect.ValueOf(new(imlApiModule)) - }) -} diff --git a/module/api/iml.go b/module/api/iml.go deleted file mode 100644 index 09215cfd..00000000 --- a/module/api/iml.go +++ /dev/null @@ -1,451 +0,0 @@ -package api - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "strings" - - "github.com/APIParkLab/APIPark/service/service" - "github.com/APIParkLab/APIPark/service/upstream" - - "gorm.io/gorm" - - "github.com/APIParkLab/APIPark/service/team" - - "github.com/google/uuid" - - "github.com/eolinker/go-common/auto" - "github.com/eolinker/go-common/utils" - - "github.com/eolinker/go-common/store" - - "github.com/APIParkLab/APIPark/service/api" - - api_dto "github.com/APIParkLab/APIPark/module/api/dto" -) - -var _ IApiModule = (*imlApiModule)(nil) -var ( - asServer = map[string]bool{ - "as_server": true, - } -) - -type imlApiModule struct { - teamService team.ITeamService `autowired:""` - serviceService service.IServiceService `autowired:""` - apiService api.IAPIService `autowired:""` - upstreamService upstream.IUpstreamService `autowired:""` - transaction store.ITransaction `autowired:""` -} - -func (i *imlApiModule) SimpleList(ctx context.Context, serviceId string) ([]*api_dto.ApiSimpleItem, error) { - - list, err := i.apiService.ListForService(ctx, serviceId) - apiInfos, err := i.apiService.ListInfo(ctx, utils.SliceToSlice(list, func(s *api.API) string { - return s.UUID - })...) - if err != nil { - return nil, err - } - - out := utils.SliceToSlice(apiInfos, func(item *api.Info) *api_dto.ApiSimpleItem { - return &api_dto.ApiSimpleItem{ - Id: item.UUID, - Name: item.Name, - Method: item.Method, - Path: item.Path, - } - }) - return out, nil -} - -func (i *imlApiModule) Detail(ctx context.Context, serviceId string, apiId string) (*api_dto.ApiDetail, error) { - _, err := i.serviceService.Check(ctx, serviceId, asServer) - if err != nil { - return nil, err - } - - detail, err := i.apiService.GetInfo(ctx, apiId) - if err != nil { - return nil, err - } - - apiDetail := &api_dto.ApiDetail{ - ApiSimpleDetail: *api_dto.GenApiSimpleDetail(detail), - } - proxy, err := i.apiService.LatestProxy(ctx, apiId) - if err != nil { - if !errors.Is(err, gorm.ErrRecordNotFound) { - return nil, err - } - } - if proxy != nil { - - apiDetail.Proxy = api_dto.FromServiceProxy(proxy.Data) - } - - document, err := i.apiService.LatestDocument(ctx, apiId) - if err != nil { - if !errors.Is(err, gorm.ErrRecordNotFound) { - return nil, err - } - } - if document != nil { - doc := make(map[string]interface{}) - err = json.Unmarshal([]byte(document.Data.Content), &doc) - if err != nil { - return nil, err - } - apiDetail.Doc = doc - } - - return apiDetail, nil -} - -func (i *imlApiModule) SimpleDetail(ctx context.Context, serviceId string, apiId string) (*api_dto.ApiSimpleDetail, error) { - _, err := i.serviceService.Check(ctx, serviceId, asServer) - if err != nil { - return nil, err - } - - detail, err := i.apiService.GetInfo(ctx, apiId) - if err != nil { - return nil, err - } - - return api_dto.GenApiSimpleDetail(detail), nil -} - -func (i *imlApiModule) Search(ctx context.Context, keyword string, serviceId string) ([]*api_dto.ApiItem, error) { - _, err := i.serviceService.Check(ctx, serviceId, asServer) - if err != nil { - return nil, err - } - - list, err := i.apiService.Search(ctx, keyword, map[string]interface{}{ - "service": serviceId, - }) - if err != nil { - return nil, err - } - apiInfos, err := i.apiService.ListInfo(ctx, utils.SliceToSlice(list, func(s *api.API) string { - return s.UUID - })...) - if err != nil { - return nil, err - } - utils.Sort(apiInfos, func(a, b *api.Info) bool { - return a.UpdateAt.After(b.UpdateAt) - }) - out := utils.SliceToSlice(apiInfos, func(item *api.Info) *api_dto.ApiItem { - return &api_dto.ApiItem{ - Id: item.UUID, - Name: item.Name, - Method: item.Method, - Path: item.Path, - Creator: auto.UUID(item.Creator), - Updater: auto.UUID(item.Updater), - CreateTime: auto.TimeLabel(item.CreateAt), - UpdateTime: auto.TimeLabel(item.UpdateAt), - CanDelete: true, - } - }) - - return out, nil -} - -func (i *imlApiModule) SimpleSearch(ctx context.Context, keyword string, serviceId string) ([]*api_dto.ApiSimpleItem, error) { - _, err := i.serviceService.Check(ctx, serviceId, asServer) - if err != nil { - return nil, err - } - - list, err := i.apiService.Search(ctx, keyword, map[string]interface{}{ - "service": serviceId, - }) - if err != nil { - return nil, err - } - apiInfos, err := i.apiService.ListInfo(ctx, utils.SliceToSlice(list, func(s *api.API) string { - return s.UUID - })...) - if err != nil { - return nil, err - } - out := utils.SliceToSlice(apiInfos, func(item *api.Info) *api_dto.ApiSimpleItem { - return &api_dto.ApiSimpleItem{ - Id: item.UUID, - Name: item.Name, - Method: item.Method, - Path: item.Path, - } - }) - return out, nil -} - -func (i *imlApiModule) Create(ctx context.Context, serviceId string, dto *api_dto.CreateApi) (*api_dto.ApiSimpleDetail, error) { - info, err := i.serviceService.Check(ctx, serviceId, asServer) - if err != nil { - return nil, err - } - prefix, err := i.Prefix(ctx, serviceId) - if err != nil { - return nil, err - } - err = i.transaction.Transaction(ctx, func(ctx context.Context) error { - if dto.Id == "" { - dto.Id = uuid.New().String() - } - err = dto.Validate() - if err != nil { - return err - } - - path := fmt.Sprintf("%s%s", prefix, dto.Path) - err = i.apiService.Exist(ctx, "", &api.ExistAPI{Path: dto.Path, Method: dto.Method}) - if err != nil { - return fmt.Errorf("api path %s,method: %s already exist", dto.Path, dto.Method) - } - proxy := api_dto.ToServiceProxy(dto.Proxy) - err = i.apiService.SaveProxy(ctx, dto.Id, proxy) - if err != nil { - return err - } - err = i.apiService.SaveDocument(ctx, dto.Id, api_dto.ToServiceDocument(nil)) - if err != nil { - return err - } - - match, _ := json.Marshal(dto.MatchRules) - return i.apiService.Create(ctx, &api.CreateAPI{ - UUID: dto.Id, - Name: dto.Name, - Description: dto.Description, - Service: serviceId, - Team: info.Team, - Method: dto.Method, - Path: path, - Match: string(match), - //Upstream: proxy.Upstream, - }) - }) - if err != nil { - return nil, err - } - return i.SimpleDetail(ctx, serviceId, dto.Id) -} - -func (i *imlApiModule) Edit(ctx context.Context, serviceId string, apiId string, dto *api_dto.EditApi) (*api_dto.ApiSimpleDetail, error) { - _, err := i.serviceService.Check(ctx, serviceId, asServer) - if err != nil { - return nil, err - } - - err = i.transaction.Transaction(ctx, func(ctx context.Context) error { - var up *string - if dto.Proxy != nil { - err = i.apiService.SaveProxy(ctx, apiId, api_dto.ToServiceProxy(dto.Proxy)) - if err != nil { - return err - } - //if dto.Proxy.Upstream != "" { - // up = &dto.Proxy.Upstream - //} - } - err = i.apiService.Save(ctx, apiId, &api.EditAPI{ - Name: dto.Info.Name, - Description: dto.Info.Description, - Upstream: up, - }) - if err != nil { - return err - } - - if dto.Doc != nil { - err = i.apiService.SaveDocument(ctx, apiId, api_dto.ToServiceDocument(*dto.Doc)) - if err != nil { - return err - } - } - return nil - - }) - if err != nil { - return nil, err - } - return i.SimpleDetail(ctx, serviceId, apiId) -} - -func (i *imlApiModule) Delete(ctx context.Context, serviceId string, apiId string) error { - _, err := i.serviceService.Check(ctx, serviceId, asServer) - if err != nil { - return err - } - return i.apiService.Delete(ctx, apiId) -} - -func (i *imlApiModule) Copy(ctx context.Context, serviceId string, apiId string, dto *api_dto.CreateApi) (*api_dto.ApiSimpleDetail, error) { - info, err := i.serviceService.Check(ctx, serviceId, asServer) - if err != nil { - return nil, err - } - oldApi, err := i.apiService.Get(ctx, apiId) - if err != nil { - return nil, err - } - prefix, err := i.Prefix(ctx, serviceId) - if err != nil { - return nil, err - } - err = i.transaction.Transaction(ctx, func(ctx context.Context) error { - if dto.Id == "" { - dto.Id = uuid.New().String() - } - err = dto.Validate() - if err != nil { - return err - } - - path := fmt.Sprintf("%s/%s", strings.TrimSuffix(prefix, "/"), strings.TrimPrefix(dto.Path, "/")) - err = i.apiService.Exist(ctx, serviceId, &api.ExistAPI{Path: path, Method: dto.Method}) - if err != nil { - return err - } - - proxy, err := i.apiService.LatestProxy(ctx, oldApi.UUID) - if err != nil { - if !errors.Is(err, gorm.ErrRecordNotFound) { - return err - } - } - //upstreamId := "" - if proxy != nil { - err = i.apiService.SaveProxy(ctx, dto.Id, proxy.Data) - if err != nil { - return err - } - //upstreamId = proxy.Data.Upstream - } - - doc, err := i.apiService.LatestDocument(ctx, oldApi.UUID) - if err != nil { - if !errors.Is(err, gorm.ErrRecordNotFound) { - return err - } - } - if doc != nil { - err = i.apiService.SaveDocument(ctx, dto.Id, doc.Data) - if err != nil { - return err - } - } - match, _ := json.Marshal(dto.MatchRules) - return i.apiService.Create(ctx, &api.CreateAPI{ - UUID: dto.Id, - Name: dto.Name, - Service: serviceId, - Team: info.Team, - Method: dto.Method, - Path: path, - Match: string(match), - //Upstream: upstreamId, - }) - - }) - if err != nil { - return nil, err - } - return i.SimpleDetail(ctx, serviceId, dto.Id) -} - -func (i *imlApiModule) ApiDocDetail(ctx context.Context, serviceId string, apiId string) (*api_dto.ApiDocDetail, error) { - _, err := i.serviceService.Check(ctx, serviceId, asServer) - if err != nil { - return nil, err - } - - apiBase, err := i.apiService.Get(ctx, apiId) - if err != nil { - return nil, err - } - if apiBase.IsDelete { - return nil, errors.New("api is delete") - } - - detail, err := i.apiService.GetInfo(ctx, apiBase.UUID) - if err != nil { - return nil, err - } - document, err := i.apiService.LatestDocument(ctx, apiId) - if err != nil { - if !errors.Is(err, gorm.ErrRecordNotFound) { - return nil, err - } - } - var doc map[string]interface{} - if document != nil { - doc = make(map[string]interface{}) - err = json.Unmarshal([]byte(document.Data.Content), &doc) - if err != nil { - return nil, err - } - } - return &api_dto.ApiDocDetail{ - ApiSimpleDetail: *api_dto.GenApiSimpleDetail(detail), - Doc: doc, - }, nil -} - -func (i *imlApiModule) ApiProxyDetail(ctx context.Context, serviceId string, apiId string) (*api_dto.ApiProxyDetail, error) { - _, err := i.serviceService.Check(ctx, serviceId, asServer) - if err != nil { - return nil, err - } - apiBase, err := i.apiService.Get(ctx, apiId) - if err != nil { - return nil, err - } - if apiBase.IsDelete { - return nil, errors.New("api is delete") - } - if apiBase.Service != serviceId { - return nil, errors.New("api is not in project") - } - - detail, err := i.apiService.GetInfo(ctx, apiId) - if err != nil { - return nil, err - } - - apiDetail := &api_dto.ApiProxyDetail{ - ApiSimpleDetail: *api_dto.GenApiSimpleDetail(detail), - } - proxy, err := i.apiService.LatestProxy(ctx, apiId) - if err != nil { - if !errors.Is(err, gorm.ErrRecordNotFound) { - return nil, err - } - } - if proxy != nil { - apiDetail.Proxy = api_dto.FromServiceProxy(proxy.Data) - } - return apiDetail, nil - -} - -func (i *imlApiModule) Prefix(ctx context.Context, serviceId string) (string, error) { - pInfo, err := i.serviceService.Check(ctx, serviceId, asServer) - if err != nil { - return "", err - } - - if pInfo.Prefix != "" { - if pInfo.Prefix[0] != '/' { - pInfo.Prefix = fmt.Sprintf("/%s", strings.TrimSuffix(pInfo.Prefix, "/")) - } - } - return strings.TrimSuffix(pInfo.Prefix, "/"), nil -} diff --git a/module/application-authorization/authorization.go b/module/application-authorization/authorization.go index 847528b8..56026253 100644 --- a/module/application-authorization/authorization.go +++ b/module/application-authorization/authorization.go @@ -2,14 +2,15 @@ package application_authorization import ( "context" + "github.com/APIParkLab/APIPark/module/system" "reflect" - + application_authorization_dto "github.com/APIParkLab/APIPark/module/application-authorization/dto" - + "github.com/APIParkLab/APIPark/gateway" - + "github.com/eolinker/go-common/autowire" - + _ "github.com/APIParkLab/APIPark/module/application-authorization/auth-driver/aksk" _ "github.com/APIParkLab/APIPark/module/application-authorization/auth-driver/apikey" _ "github.com/APIParkLab/APIPark/module/application-authorization/auth-driver/basic" @@ -30,12 +31,21 @@ type IAuthorizationModule interface { Detail(ctx context.Context, appId string, aid string) ([]application_authorization_dto.DetailItem, error) // Info 获取项目鉴权详情 Info(ctx context.Context, appId string, aid string) (*application_authorization_dto.Authorization, error) + //ExportAll(ctx context.Context) ([]*application_authorization_dto.ExportAuthorization, error) +} + +type IExportAuthorizationModule interface { + system.IExportModule[application_authorization_dto.ExportAuthorization] } func init() { + authModule := new(imlAuthorizationModule) autowire.Auto[IAuthorizationModule](func() reflect.Value { - m := new(imlAuthorizationModule) - gateway.RegisterInitHandleFunc(m.initGateway) - return reflect.ValueOf(m) + gateway.RegisterInitHandleFunc(authModule.initGateway) + return reflect.ValueOf(authModule) + }) + + autowire.Auto[IExportAuthorizationModule](func() reflect.Value { + return reflect.ValueOf(authModule) }) } diff --git a/module/application-authorization/dto/output.go b/module/application-authorization/dto/output.go index 505b31bd..41756be3 100644 --- a/module/application-authorization/dto/output.go +++ b/module/application-authorization/dto/output.go @@ -31,3 +31,15 @@ type DetailItem struct { Key string `json:"key"` Value string `json:"value"` } + +type ExportAuthorization struct { + Application string `json:"application"` + UUID string `json:"id"` + Name string `json:"name"` + Driver string `json:"driver"` + Position string `json:"position"` + TokenName string `json:"token_name"` + Config map[string]interface{} `json:"config"` + ExpireTime int64 `json:"expire_time"` + HideCredential bool `json:"hide_credential"` +} diff --git a/module/application-authorization/iml.go b/module/application-authorization/iml.go index e8b25e44..92efe468 100644 --- a/module/application-authorization/iml.go +++ b/module/application-authorization/iml.go @@ -6,30 +6,33 @@ import ( "errors" "fmt" "time" - + application_authorization "github.com/APIParkLab/APIPark/service/application-authorization" - + "github.com/eolinker/eosc/log" - + authDriver "github.com/APIParkLab/APIPark/module/application-authorization/auth-driver" - + "github.com/eolinker/go-common/utils" - + "github.com/APIParkLab/APIPark/gateway" - + "github.com/APIParkLab/APIPark/service/cluster" "github.com/APIParkLab/APIPark/service/service" - + "github.com/eolinker/go-common/auto" - + "github.com/google/uuid" - + "github.com/eolinker/go-common/store" - + application_authorization_dto "github.com/APIParkLab/APIPark/module/application-authorization/dto" ) -var _ IAuthorizationModule = (*imlAuthorizationModule)(nil) +var ( + _ IAuthorizationModule = (*imlAuthorizationModule)(nil) + _ IExportAuthorizationModule = (*imlAuthorizationModule)(nil) +) type imlAuthorizationModule struct { serviceService service.IServiceService `autowired:""` @@ -38,6 +41,29 @@ type imlAuthorizationModule struct { transaction store.ITransaction `autowired:""` } +func (i *imlAuthorizationModule) ExportAll(ctx context.Context) ([]*application_authorization_dto.ExportAuthorization, error) { + list, err := i.authorizationService.List(ctx) + if err != nil { + return nil, err + } + + return utils.SliceToSlice(list, func(a *application_authorization.Authorization) *application_authorization_dto.ExportAuthorization { + cfg := make(map[string]interface{}) + json.Unmarshal([]byte(a.Config), &cfg) + return &application_authorization_dto.ExportAuthorization{ + Application: a.Application, + UUID: a.UUID, + Name: a.Name, + Driver: a.Type, + Position: a.Position, + TokenName: a.TokenName, + Config: cfg, + ExpireTime: a.ExpireTime, + HideCredential: a.HideCredential, + } + }), nil +} + func (i *imlAuthorizationModule) getApplications(ctx context.Context, appIds []string, appMap map[string]*service.Service) ([]*gateway.ApplicationRelease, error) { authorizations, err := i.authorizationService.ListByApp(ctx, appIds...) if err != nil { @@ -62,7 +88,7 @@ func (i *imlAuthorizationModule) getApplications(ctx context.Context, appIds []s "service": id, }, }, - + Authorizations: utils.SliceToSlice(auths, func(a *application_authorization.Authorization) *gateway.Authorization { authCfg := make(map[string]interface{}) _ = json.Unmarshal([]byte(a.Config), &authCfg) @@ -90,7 +116,7 @@ func (i *imlAuthorizationModule) initGateway(ctx context.Context, partitionId st serviceIds = append(serviceIds, p.Id) serviceMap[p.Id] = p } - + applications, err := i.getApplications(ctx, serviceIds, serviceMap) if err != nil { return err @@ -99,7 +125,7 @@ func (i *imlAuthorizationModule) initGateway(ctx context.Context, partitionId st } func (i *imlAuthorizationModule) online(ctx context.Context, s *service.Service) error { - + clusters, err := i.clusterService.List(ctx) if err != nil { return err @@ -130,7 +156,7 @@ func (i *imlAuthorizationModule) online(ctx context.Context, s *service.Service) } }), } - + for _, c := range clusters { err := i.doOnline(ctx, c.Uuid, app) if err != nil { @@ -148,7 +174,7 @@ func (i *imlAuthorizationModule) doOnline(ctx context.Context, clusterId string, _ = client.Close(ctx) }() return client.Application().Online(ctx, app) - + } func (i *imlAuthorizationModule) AddAuthorization(ctx context.Context, appId string, info *application_authorization_dto.CreateAuthorization) (*application_authorization_dto.Authorization, error) { authFactory, has := authDriver.GetAuthFactory(info.Driver) @@ -163,16 +189,16 @@ func (i *imlAuthorizationModule) AddAuthorization(ctx context.Context, appId str if err != nil { return nil, err } - + s, err := i.serviceService.Get(ctx, appId) if err != nil { return nil, err } - + if info.UUID == "" { info.UUID = uuid.New().String() } - + // 缺少配置查重操作 err = i.transaction.Transaction(ctx, func(ctx context.Context) error { err = i.authorizationService.Create(ctx, &application_authorization.Create{ @@ -190,13 +216,13 @@ func (i *imlAuthorizationModule) AddAuthorization(ctx context.Context, appId str if err != nil { return err } - + return i.online(ctx, s) }) if err != nil { return nil, err } - + return i.Info(ctx, appId, info.UUID) } @@ -217,12 +243,12 @@ func (i *imlAuthorizationModule) EditAuthorization(ctx context.Context, appId st if err != nil { return nil, err } - + appInfo, err := i.serviceService.Get(ctx, appId) if err != nil { return nil, err } - + err = i.transaction.Transaction(ctx, func(ctx context.Context) error { authId := auth.GenerateID(authInfo.Position, authInfo.TokenName) cfgStr := string(cfg) @@ -240,7 +266,7 @@ func (i *imlAuthorizationModule) EditAuthorization(ctx context.Context, appId st } return i.online(ctx, appInfo) }) - + if err != nil { return nil, err } @@ -252,7 +278,7 @@ func (i *imlAuthorizationModule) DeleteAuthorization(ctx context.Context, pid st if err != nil { return err } - + return i.transaction.Transaction(ctx, func(ctx context.Context) error { err = i.authorizationService.Delete(ctx, aid) if err != nil { @@ -285,7 +311,7 @@ func (i *imlAuthorizationModule) doOffline(ctx context.Context, clusterId string _ = client.Close(ctx) }() return client.Application().Offline(ctx, app) - + } func (i *imlAuthorizationModule) Authorizations(ctx context.Context, pid string) ([]*application_authorization_dto.AuthorizationItem, error) { _, err := i.serviceService.Get(ctx, pid) @@ -349,7 +375,7 @@ func (i *imlAuthorizationModule) Detail(ctx context.Context, pid string, aid str hideAuthStr = "否" } details = append(details, application_authorization_dto.DetailItem{Key: "隐藏鉴权信息", Value: hideAuthStr}) - + return details, nil } @@ -366,7 +392,7 @@ func (i *imlAuthorizationModule) Info(ctx context.Context, pid string, aid strin if auth.Config != "" { _ = json.Unmarshal([]byte(auth.Config), &cfg) } - + return &application_authorization_dto.Authorization{ UUID: auth.UUID, Name: auth.Name, diff --git a/module/catalogue/catalogue.go b/module/catalogue/catalogue.go index 7ef4ab5b..eacddd7f 100644 --- a/module/catalogue/catalogue.go +++ b/module/catalogue/catalogue.go @@ -2,10 +2,11 @@ package catalogue import ( "context" + "github.com/APIParkLab/APIPark/module/system" "reflect" - + "github.com/eolinker/go-common/autowire" - + catalogue_dto "github.com/APIParkLab/APIPark/module/catalogue/dto" ) @@ -16,6 +17,8 @@ type ICatalogueModule interface { Create(ctx context.Context, input *catalogue_dto.CreateCatalogue) error // Edit 编辑目录 Edit(ctx context.Context, id string, input *catalogue_dto.EditCatalogue) error + + Get(ctx context.Context, id string) (*catalogue_dto.Catalogue, error) // Delete 删除目录 Delete(ctx context.Context, id string) error // Services 关键字筛选服务列表 @@ -25,10 +28,20 @@ type ICatalogueModule interface { // Subscribe 订阅服务 Subscribe(ctx context.Context, subscribeInfo *catalogue_dto.SubscribeService) error Sort(ctx context.Context, sorts []*catalogue_dto.SortItem) error + //ExportAll(ctx context.Context) ([]*catalogue_dto.ExportCatalogue, error) +} + +type IExportCatalogueModule interface { + system.IExportModule[catalogue_dto.ExportCatalogue] } func init() { + catalogueModule := new(imlCatalogueModule) autowire.Auto[ICatalogueModule](func() reflect.Value { - return reflect.ValueOf(new(imlCatalogueModule)) + return reflect.ValueOf(catalogueModule) + }) + + autowire.Auto[IExportCatalogueModule](func() reflect.Value { + return reflect.ValueOf(catalogueModule) }) } diff --git a/module/catalogue/dto/input.go b/module/catalogue/dto/input.go index 11fb91b7..dfd987f3 100644 --- a/module/catalogue/dto/input.go +++ b/module/catalogue/dto/input.go @@ -4,11 +4,13 @@ type CreateCatalogue struct { Id string `json:"id"` Name string `json:"name"` Parent *string `json:"parent" aocheck:"catalogue"` + Sort *int `json:"sort"` } type EditCatalogue struct { Name *string `json:"name"` Parent *string `json:"parent" aocheck:"catalogue"` + Sort *int `json:"sort"` } type SubscribeService struct { diff --git a/module/catalogue/dto/output.go b/module/catalogue/dto/output.go index f3ca59b4..5ad0913a 100644 --- a/module/catalogue/dto/output.go +++ b/module/catalogue/dto/output.go @@ -25,7 +25,8 @@ type ServiceDetail struct { Description string `json:"description"` Document string `json:"document"` Basic *ServiceBasic `json:"basic"` - Apis []*ServiceApi `json:"apis"` + //Apis []*ServiceApi `json:"apis"` + APIDoc string `json:"api_doc"` } type ServiceBasic struct { @@ -61,3 +62,17 @@ type Partition struct { Name string `json:"name"` Prefix string `json:"prefix"` } + +type Catalogue struct { + Id string `json:"id"` + Name string `json:"name"` + Parent string `json:"parent"` + Sort int `json:"sort"` +} + +type ExportCatalogue struct { + Id string `json:"id"` + Name string `json:"name"` + Parent string `json:"parent"` + Sort int `json:"sort"` +} diff --git a/module/catalogue/iml.go b/module/catalogue/iml.go index 6a98f17c..08bcb2d4 100644 --- a/module/catalogue/iml.go +++ b/module/catalogue/iml.go @@ -2,37 +2,37 @@ package catalogue import ( "context" - "encoding/json" "errors" "fmt" + api_doc "github.com/APIParkLab/APIPark/service/api-doc" "math" "sort" - + service_doc "github.com/APIParkLab/APIPark/service/service-doc" - + service_tag "github.com/APIParkLab/APIPark/service/service-tag" - + "github.com/APIParkLab/APIPark/service/subscribe" - + "github.com/eolinker/go-common/store" - + "gorm.io/gorm" - + "github.com/eolinker/go-common/utils" - + "github.com/APIParkLab/APIPark/service/release" - + "github.com/APIParkLab/APIPark/service/api" "github.com/eolinker/go-common/auto" - + "github.com/APIParkLab/APIPark/service/tag" - + "github.com/APIParkLab/APIPark/service/service" - + "github.com/google/uuid" - + "github.com/APIParkLab/APIPark/service/catalogue" - + catalogue_dto "github.com/APIParkLab/APIPark/module/catalogue/dto" ) @@ -44,6 +44,7 @@ var ( type imlCatalogueModule struct { catalogueService catalogue.ICatalogueService `autowired:""` apiService api.IAPIService `autowired:""` + apiDocService api_doc.IAPIDocService `autowired:""` serviceService service.IServiceService `autowired:""` serviceTagService service_tag.ITagService `autowired:""` serviceDocService service_doc.IDocService `autowired:""` @@ -52,10 +53,39 @@ type imlCatalogueModule struct { subscribeService subscribe.ISubscribeService `autowired:""` subscribeApplyService subscribe.ISubscribeApplyService `autowired:""` transaction store.ITransaction `autowired:""` - + root *Root } +func (i *imlCatalogueModule) Get(ctx context.Context, id string) (*catalogue_dto.Catalogue, error) { + info, err := i.catalogueService.Get(ctx, id) + if err != nil { + return nil, err + } + return &catalogue_dto.Catalogue{ + Id: info.Id, + Name: info.Name, + Parent: info.Parent, + Sort: info.Sort, + }, nil +} + +func (i *imlCatalogueModule) ExportAll(ctx context.Context) ([]*catalogue_dto.ExportCatalogue, error) { + list, err := i.catalogueService.List(ctx) + if err != nil { + return nil, err + } + + return utils.SliceToSlice(list, func(c *catalogue.Catalogue) *catalogue_dto.ExportCatalogue { + return &catalogue_dto.ExportCatalogue{ + Id: c.Id, + Name: c.Name, + Parent: c.Parent, + Sort: c.Sort, + } + }), nil +} + func (i *imlCatalogueModule) Subscribe(ctx context.Context, subscribeInfo *catalogue_dto.SubscribeService) error { if len(subscribeInfo.Applications) == 0 { return fmt.Errorf("applications is empty") @@ -68,18 +98,18 @@ func (i *imlCatalogueModule) Subscribe(ctx context.Context, subscribeInfo *catal if !s.AsServer { return fmt.Errorf("service does not support subscribe") } - + userId := utils.UserId(ctx) return i.transaction.Transaction(ctx, func(ctx context.Context) error { - + apps := make([]string, 0, len(subscribeInfo.Applications)) - + for _, appId := range subscribeInfo.Applications { if appId == s.Id { // 不能订阅自己 continue } - + appInfo, err := i.serviceService.Get(ctx, appId) if err != nil { return err @@ -88,11 +118,7 @@ func (i *imlCatalogueModule) Subscribe(ctx context.Context, subscribeInfo *catal // 当系统不可作为订阅方时,不可订阅 continue } - //info, err := i.subscribeApplyService.GetApply(ctx, subscribeInfo.Service, appId) - //if err != nil { - // if !errors.Is(err, gorm.ErrRecordNotFound) { - // return err - // } + err = i.subscribeApplyService.Create(ctx, &subscribe.CreateApply{ Uuid: uuid.New().String(), Service: subscribeInfo.Service, @@ -103,18 +129,11 @@ func (i *imlCatalogueModule) Subscribe(ctx context.Context, subscribeInfo *catal Status: subscribe.ApplyStatusReview, Applier: userId, }) - - //} else { - // status := subscribe.ApplyStatusReview - // err = i.subscribeApplyService.Save(ctx, info.Id, &subscribe.EditApply{ - // Status: &status, - // Applier: &userId, - // }) - //} + if err != nil { return err } - + // 修改订阅表状态 subscribers, err := i.subscribeService.ListByApplication(ctx, subscribeInfo.Service, appId) if err != nil { @@ -131,7 +150,7 @@ func (i *imlCatalogueModule) Subscribe(ctx context.Context, subscribeInfo *catal if err != nil { return err } - + } else { subscriberMap := utils.SliceToMap(subscribers, func(t *subscribe.Subscribe) string { return t.Application @@ -154,9 +173,9 @@ func (i *imlCatalogueModule) Subscribe(ctx context.Context, subscribeInfo *catal ApplyStatus: &status, }) } - + } - + apps = append(apps, appId) } if len(apps) == 0 { @@ -164,7 +183,7 @@ func (i *imlCatalogueModule) Subscribe(ctx context.Context, subscribeInfo *catal } return nil }) - + } func (i *imlCatalogueModule) ServiceDetail(ctx context.Context, sid string) (*catalogue_dto.ServiceDetail, error) { @@ -173,81 +192,23 @@ func (i *imlCatalogueModule) ServiceDetail(ctx context.Context, sid string) (*ca if err != nil { return nil, fmt.Errorf("get service failed: %w", err) } - docStr := "" - doc, err := i.serviceDocService.Get(ctx, sid) - if err != nil { - if !errors.Is(err, gorm.ErrRecordNotFound) { - return nil, fmt.Errorf("get service doc failed: %w", err) - } - } else { - docStr = doc.Doc - } - r, err := i.releaseService.GetRunning(ctx, s.Id) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return &catalogue_dto.ServiceDetail{ Name: s.Name, Description: s.Description, - Document: docStr, + Document: "", Basic: &catalogue_dto.ServiceBasic{ Team: auto.UUID(s.Team), ApiNum: 0, }, }, nil } - + return nil, fmt.Errorf("get running release failed: %w", err) } - _, docCommits, _, err := i.releaseService.GetReleaseInfos(ctx, r.UUID) - if err != nil { - return nil, fmt.Errorf("get release apis failed: %w", err) - } - apiMap := make(map[string]*release.APIDocumentCommit) - apiIds := make([]string, 0, len(docCommits)) - for _, v := range docCommits { - apiIds = append(apiIds, v.API) - apiMap[v.API] = v - } - apiList, err := i.apiService.ListInfo(ctx, apiIds...) - if err != nil { - return nil, err - } - - apis := make([]*catalogue_dto.ServiceApi, 0, len(apiList)) - for _, info := range apiList { - basicApi := &catalogue_dto.ServiceApiBasic{ - Id: info.UUID, - Name: info.Name, - Description: info.Description, - Method: info.Method, - Path: info.Path, - Creator: auto.UUID(info.Creator), - Updater: auto.UUID(info.Updater), - CreateTime: auto.TimeLabel(info.CreateAt), - UpdateTime: auto.TimeLabel(info.UpdateAt), - } - v, ok := apiMap[info.UUID] - if !ok { - continue - } - commit, err := i.apiService.GetDocumentCommit(ctx, v.Commit) - if err != nil { - return nil, err - } - tmp := make(map[string]interface{}) - if commit.Data != nil { - err = json.Unmarshal([]byte(commit.Data.Content), &tmp) - if err != nil { - return nil, err - } - } - - apis = append(apis, &catalogue_dto.ServiceApi{ - ServiceApiBasic: basicApi, - Doc: tmp, - }) - } + countMap, err := i.subscribeService.CountMapByService(ctx, subscribe.ApplyStatusSubscribe, sid) if err != nil { return nil, err @@ -261,13 +222,36 @@ func (i *imlCatalogueModule) ServiceDetail(ctx context.Context, sid string) (*ca }, func(t *service_tag.Tag) bool { return t.Sid == sid }) + _, _, apiDocCommit, _, serviceDocCommit, err := i.releaseService.GetReleaseInfos(ctx, r.UUID) + if err != nil { + return nil, fmt.Errorf("get release apis failed: %w", err) + } + var apiDoc string + var apiNum int + if apiDocCommit != nil { + commit, err := i.apiDocService.GetDocCommit(ctx, apiDocCommit.Commit) + if err != nil { + return nil, err + } + apiDoc = commit.Data.Content + apiNum = int(commit.Data.APICount) + } + + var serviceDoc string + if serviceDocCommit != nil { + commit, err := i.serviceDocService.GetDocCommit(ctx, serviceDocCommit.Commit) + if err != nil { + return nil, err + } + serviceDoc = commit.Data.Content + } return &catalogue_dto.ServiceDetail{ Name: s.Name, Description: s.Description, - Document: docStr, + Document: serviceDoc, Basic: &catalogue_dto.ServiceBasic{ Team: auto.UUID(s.Team), - ApiNum: len(apis), + ApiNum: apiNum, AppNum: int(countMap[s.Id]), Tags: auto.List(tagIds), Catalogue: auto.UUID(s.Catalogue), @@ -275,12 +259,12 @@ func (i *imlCatalogueModule) ServiceDetail(ctx context.Context, sid string) (*ca UpdateTime: auto.TimeLabel(r.CreateAt), Logo: s.Logo, }, - Apis: apis, + APIDoc: apiDoc, }, nil } func (i *imlCatalogueModule) Services(ctx context.Context, keyword string) ([]*catalogue_dto.ServiceItem, error) { - + serviceTags, err := i.serviceTagService.List(ctx, nil, nil) if err != nil { return nil, err @@ -288,7 +272,7 @@ func (i *imlCatalogueModule) Services(ctx context.Context, keyword string) ([]*c serviceTagMap := utils.SliceToMapArrayO(serviceTags, func(t *service_tag.Tag) (string, string) { return t.Sid, t.Tid }) - + items, err := i.serviceService.SearchPublicServices(ctx, keyword) if err != nil { return nil, err @@ -303,25 +287,33 @@ func (i *imlCatalogueModule) Services(ctx context.Context, keyword string) ([]*c if len(serviceIds) < 1 { return nil, nil } - - // 获取服务API数量 - apiCountMap, err := i.apiService.CountMapByService(ctx, serviceIds...) + commits, err := i.releaseService.GetRunningApiDocCommits(ctx, serviceIds...) if err != nil { return nil, err } - + apiCountMap, err := i.apiDocService.LatestAPICountByCommits(ctx, commits...) + if err != nil { + return nil, err + } + + //// 获取服务API数量 + //apiCountMap, err := i.apiDocService.LatestAPICountByServices(ctx, serviceIds...) + //if err != nil { + // return nil, err + //} + subscriberCountMap, err := i.subscribeService.CountMapByService(ctx, subscribe.ApplyStatusSubscribe, serviceIds...) if err != nil { return nil, err } - + result := make([]*catalogue_dto.ServiceItem, 0, len(items)) for _, v := range items { apiNum, ok := apiCountMap[v.Id] if !ok || apiNum < 1 { continue } - + result = append(result, &catalogue_dto.ServiceItem{ Id: v.Id, Name: v.Name, @@ -376,7 +368,7 @@ func (i *imlCatalogueModule) Sort(ctx context.Context, sorts []*catalogue_dto.So i.root = NewRoot(all) return nil }) - + } func (i *imlCatalogueModule) Search(ctx context.Context, keyword string) ([]*catalogue_dto.Item, error) { @@ -396,7 +388,7 @@ func (i *imlCatalogueModule) Search(ctx context.Context, keyword string) ([]*cat } return treeItems("", parentMap), nil } - + catalogues, err := i.catalogueService.Search(ctx, keyword, nil) if err != nil { return nil, err @@ -406,7 +398,7 @@ func (i *imlCatalogueModule) Search(ctx context.Context, keyword string) ([]*cat i.root = NewRoot(all) } items := make([]*catalogue_dto.Item, 0, len(catalogues)) - + return items, nil } @@ -418,11 +410,15 @@ func (i *imlCatalogueModule) Create(ctx context.Context, input *catalogue_dto.Cr if input.Id == "" { input.Id = uuid.New().String() } + index := _sortMax + if input.Sort != nil { + index = *input.Sort + } err := i.catalogueService.Create(ctx, &catalogue.CreateCatalogue{ Id: input.Id, Name: input.Name, Parent: parent, - Sort: _sortMax, + Sort: index, }) if err != nil { return err @@ -440,6 +436,7 @@ func (i *imlCatalogueModule) Edit(ctx context.Context, id string, input *catalog err := i.catalogueService.Save(ctx, id, &catalogue.EditCatalogue{ Name: input.Name, Parent: input.Parent, + Sort: input.Sort, }) if err != nil { return err diff --git a/module/monitor/dto/output.go b/module/monitor/dto/output.go index 0bf769cb..0b787bf8 100644 --- a/module/monitor/dto/output.go +++ b/module/monitor/dto/output.go @@ -132,3 +132,9 @@ type MonitorConfig struct { Driver string `json:"driver"` Config map[string]interface{} `json:"config"` } + +type MonitorCluster struct { + Id string `json:"id"` + Name string `json:"name"` + Enable bool `json:"enable"` +} diff --git a/module/monitor/iml.go b/module/monitor/iml.go index 0113f6cf..0179b1ec 100644 --- a/module/monitor/iml.go +++ b/module/monitor/iml.go @@ -409,3 +409,31 @@ func (m *imlMonitorConfig) GetMonitorConfig(ctx context.Context) (*monitor_dto.M }, nil return nil, nil } + +func (m *imlMonitorConfig) GetMonitorCluster(ctx context.Context) ([]*monitor_dto.MonitorCluster, error) { + clusters, err := m.clusterService.ListByClusters(ctx) + if err != nil { + return nil, err + } + clusterIds := utils.SliceToSlice(clusters, func(i *cluster.Cluster) string { + return i.Uuid + }) + monitorMap, err := m.monitorService.MapByCluster(ctx, clusterIds...) + if err != nil { + return nil, err + } + + monitorClusters := make([]*monitor_dto.MonitorCluster, 0, len(clusters)) + for _, c := range clusters { + mc := &monitor_dto.MonitorCluster{ + Id: c.Uuid, + Name: c.Name, + } + _, ok := monitorMap[c.Uuid] + if ok { + mc.Enable = true + } + monitorClusters = append(monitorClusters, mc) + } + return monitorClusters, nil +} diff --git a/module/monitor/monitor.go b/module/monitor/monitor.go index 1a9fe2ad..5a676efb 100644 --- a/module/monitor/monitor.go +++ b/module/monitor/monitor.go @@ -30,6 +30,7 @@ type IMonitorStatisticModule interface { type IMonitorConfigModule interface { SaveMonitorConfig(ctx context.Context, cfg *monitor_dto.SaveMonitorConfig) (*monitor_dto.MonitorConfig, error) GetMonitorConfig(ctx context.Context) (*monitor_dto.MonitorConfig, error) + GetMonitorCluster(ctx context.Context) ([]*monitor_dto.MonitorCluster, error) } func init() { diff --git a/module/permit/team/iml.go b/module/permit/team/iml.go index f8d3e214..117a0960 100644 --- a/module/permit/team/iml.go +++ b/module/permit/team/iml.go @@ -23,32 +23,6 @@ type imlTeamPermitModule struct { } func (m *imlTeamPermitModule) Permissions(ctx context.Context, teamId string) ([]string, error) { - - //uid := utils.UserId(ctx) - //roleMembers, err := m.roleMemberService.List(ctx, role.TeamTarget(teamId), uid) - //if err != nil { - // return nil, err - //} - //roleIds := utils.SliceToSlice(roleMembers, func(rm *role.Member) string { - // return rm.Role - //}) - //if len(roleMembers) == 0 { - // return []string{}, nil - //} - //roles, err := m.roleService.List(ctx, roleIds...) - //if err != nil { - // return nil, err - //} - //permits := make(map[string]struct{}) - //for _, r := range roles { - // for _, p := range r.Permit { - // permits[p] = struct{}{} - // } - //} - // - //return utils.MapToSlice(permits, func(k string, v struct{}) string { - // return k - //}), nil return m.accesses(ctx, teamId) } diff --git a/module/publish/iml.go b/module/publish/iml.go index 48b0119f..0b050e59 100644 --- a/module/publish/iml.go +++ b/module/publish/iml.go @@ -5,20 +5,20 @@ import ( "errors" "fmt" "time" - + "github.com/eolinker/go-common/store" - + "github.com/APIParkLab/APIPark/service/service" - + "github.com/APIParkLab/APIPark/service/universally/commit" - + "github.com/APIParkLab/APIPark/service/api" "github.com/APIParkLab/APIPark/service/upstream" - + "github.com/APIParkLab/APIPark/gateway" - + "github.com/eolinker/eosc/log" - + "github.com/APIParkLab/APIPark/module/publish/dto" releaseModule "github.com/APIParkLab/APIPark/module/release" serviceDiff "github.com/APIParkLab/APIPark/module/service-diff" @@ -50,7 +50,7 @@ type imlPublishModule struct { } func (m *imlPublishModule) initGateway(ctx context.Context, partitionId string, clientDriver gateway.IClientDriver) error { - + projects, err := m.serviceService.List(ctx) if err != nil { return err @@ -66,7 +66,7 @@ func (m *imlPublishModule) initGateway(ctx context.Context, partitionId string, if releaseInfo == nil { continue } - + err = clientDriver.Project().Online(ctx, releaseInfo) if err != nil { return err @@ -76,7 +76,7 @@ func (m *imlPublishModule) initGateway(ctx context.Context, partitionId string, } func (m *imlPublishModule) getProjectRelease(ctx context.Context, projectID string, partitionId string) (*gateway.ProjectRelease, error) { - + releaseInfo, err := m.releaseService.GetRunning(ctx, projectID) if err != nil { if !errors.Is(err, gorm.ErrRecordNotFound) { @@ -100,12 +100,12 @@ func (m *imlPublishModule) getProjectRelease(ctx context.Context, projectID stri upstreamCommitIds = append(upstreamCommitIds, c.Commit) } } - + apiInfos, err := m.apiService.ListInfo(ctx, apiIds...) if err != nil { return nil, err } - + proxyCommits, err := m.apiService.ListProxyCommit(ctx, apiProxyCommitIds...) if err != nil { return nil, err @@ -113,7 +113,7 @@ func (m *imlPublishModule) getProjectRelease(ctx context.Context, projectID stri proxyCommitMap := utils.SliceToMapO(proxyCommits, func(c *commit.Commit[api.Proxy]) (string, *api.Proxy) { return c.Target, c.Data }) - + upstreamCommits, err := m.upstreamService.ListCommit(ctx, upstreamCommitIds...) if err != nil { return nil, err @@ -128,8 +128,8 @@ func (m *imlPublishModule) getProjectRelease(ctx context.Context, projectID stri Version: version, }, Path: a.Path, - Method: []string{a.Method}, - Service: a.Upstream, + Method: a.Methods, + Service: a.Service, } proxy, ok := proxyCommitMap[a.UUID] if ok { @@ -167,7 +167,7 @@ func (m *imlPublishModule) getProjectRelease(ctx context.Context, projectID stri }), } } - + return &gateway.ProjectRelease{ Id: projectID, Version: version, @@ -193,12 +193,12 @@ func (m *imlPublishModule) getReleaseInfo(ctx context.Context, projectID, releas upstreamCommitIds = append(upstreamCommitIds, c.Commit) } } - + apiInfos, err := m.apiService.ListInfo(ctx, apiIds...) if err != nil { return nil, err } - + proxyCommits, err := m.apiService.ListProxyCommit(ctx, apiProxyCommitIds...) if err != nil { return nil, err @@ -206,7 +206,7 @@ func (m *imlPublishModule) getReleaseInfo(ctx context.Context, projectID, releas proxyCommitMap := utils.SliceToMapO(proxyCommits, func(c *commit.Commit[api.Proxy]) (string, *api.Proxy) { return c.Target, c.Data }) - + upstreamCommits, err := m.upstreamService.ListCommit(ctx, upstreamCommitIds...) if err != nil { return nil, err @@ -220,8 +220,8 @@ func (m *imlPublishModule) getReleaseInfo(ctx context.Context, projectID, releas Version: version, }, Path: a.Path, - Method: []string{a.Method}, - Service: a.Upstream, + Method: a.Methods, + Service: a.Service, } proxy, ok := proxyCommitMap[a.UUID] if ok { @@ -246,7 +246,7 @@ func (m *imlPublishModule) getReleaseInfo(ctx context.Context, projectID, releas } projectReleaseMap := make(map[string]*gateway.ProjectRelease) upstreamReleaseMap := make(map[string]*gateway.UpstreamRelease) - + for _, c := range upstreamCommits { for _, partitionId := range clusterIds { upstreamRelease := &gateway.UpstreamRelease{ @@ -265,11 +265,11 @@ func (m *imlPublishModule) getReleaseInfo(ctx context.Context, projectID, releas return fmt.Sprintf("%s weight=%d", n.Address, n.Weight) }), } - + upstreamReleaseMap[partitionId] = upstreamRelease } } - + for _, clusterId := range clusterIds { projectReleaseMap[clusterId] = &gateway.ProjectRelease{ Id: projectID, @@ -309,7 +309,7 @@ func (m *imlPublishModule) PublishStatuses(ctx context.Context, serviceId string Status: status.String(), Error: errMsg, } - + }), nil } @@ -326,23 +326,23 @@ func (m *imlPublishModule) Apply(ctx context.Context, serviceId string, input *d if err != nil { return nil, err } - + previous := "" running, err := m.releaseService.GetRunning(ctx, serviceId) if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { - + return nil, err } if running != nil { previous = running.UUID } - + releaseToPublish, err := m.releaseService.GetRelease(ctx, input.Release) if err != nil { // 目标版本不存在 return nil, err } - + newPublishId := uuid.NewString() diff, ok, err := m.projectDiffModule.DiffForLatest(ctx, serviceId, previous) if err != nil { @@ -371,7 +371,7 @@ func (m *imlPublishModule) CheckPublish(ctx context.Context, serviceId string, r if err != nil { return nil, err } - + running, err := m.releaseService.GetRunning(ctx, serviceId) if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { return nil, err @@ -395,7 +395,7 @@ func (m *imlPublishModule) CheckPublish(ctx context.Context, serviceId string, r } return m.projectDiffModule.Out(ctx, diff) } - + } func (m *imlPublishModule) checkPublish(ctx context.Context, serviceId string, releaseId string) error { flows, err := m.publishService.ListForStatus(ctx, serviceId, publish.StatusApply, publish.StatusAccept) @@ -409,7 +409,7 @@ func (m *imlPublishModule) checkPublish(ctx context.Context, serviceId string, r if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { return err } - + if running == nil { return nil } @@ -423,7 +423,7 @@ func (m *imlPublishModule) Close(ctx context.Context, serviceId, id string) erro if err != nil { return err } - + return nil } @@ -439,7 +439,7 @@ func (m *imlPublishModule) Stop(ctx context.Context, serviceId string, id string if flow.Service != serviceId { return errors.New("项目不一致") } - + if flow.Status != publish.StatusApply && flow.Status != publish.StatusAccept { return errors.New("只有发布中状态才能停止") } @@ -467,7 +467,7 @@ func (m *imlPublishModule) Accept(ctx context.Context, serviceId string, id stri } func (m *imlPublishModule) publish(ctx context.Context, id string, clusterId string, projectRelease *gateway.ProjectRelease) error { - + publishStatus := &publish.Status{ Publish: id, Status: publish.StatusPublishing, @@ -483,7 +483,7 @@ func (m *imlPublishModule) publish(ctx context.Context, id string, clusterId str log.Errorf("set publishing publishStatus error: %v", err) } }() - + client, err := m.clusterService.GatewayClient(ctx, clusterId) if err != nil { publishStatus.Status = publish.StatusPublishError @@ -538,13 +538,13 @@ func (m *imlPublishModule) Publish(ctx context.Context, serviceId string, id str clusterIds := utils.SliceToSlice(clusters, func(i *cluster.Cluster) string { return i.Uuid }) - + projectReleaseMap, err := m.getReleaseInfo(ctx, serviceId, flow.Release, flow.Release, clusterIds) if err != nil { return err } hasError := false - + for _, c := range clusters { err = m.publish(ctx, flow.Id, c.Uuid, projectReleaseMap[c.Uuid]) if err != nil { @@ -573,7 +573,7 @@ func (m *imlPublishModule) List(ctx context.Context, serviceId string, page, pag if err != nil { return nil, 0, err } - + return utils.SliceToSlice(list, func(s *publish.Publish) *dto.Publish { return dto.FromModel(s, "") }), total, nil @@ -612,5 +612,5 @@ func (m *imlPublishModule) Detail(ctx context.Context, serviceId string, id stri Diffs: out, PublishStatuses: publishStatuses, }, nil - + } diff --git a/module/publish/module.go b/module/publish/module.go index 94cb3780..f4f67dbf 100644 --- a/module/publish/module.go +++ b/module/publish/module.go @@ -3,17 +3,16 @@ package publish import ( "context" "reflect" - + "github.com/APIParkLab/APIPark/gateway" - + "github.com/APIParkLab/APIPark/module/publish/dto" "github.com/eolinker/go-common/autowire" ) type IPublishModule interface { CheckPublish(ctx context.Context, serviceId string, releaseId string) (*dto.DiffOut, error) - //ReleaseDo(ctx context.Context, serviceId string, input *dto.ApplyOnReleaseInput) error - + Apply(ctx context.Context, serviceId string, input *dto.ApplyInput) (*dto.Publish, error) Stop(ctx context.Context, serviceId string, id string) error Refuse(ctx context.Context, serviceId string, id string, commits string) error diff --git a/module/release/iml.go b/module/release/iml.go index 3e0333a4..a4e4769f 100644 --- a/module/release/iml.go +++ b/module/release/iml.go @@ -4,11 +4,13 @@ import ( "context" "errors" "fmt" - + api_doc "github.com/APIParkLab/APIPark/service/api-doc" + service_doc "github.com/APIParkLab/APIPark/service/service-doc" + "github.com/APIParkLab/APIPark/service/cluster" "github.com/APIParkLab/APIPark/service/service" "github.com/APIParkLab/APIPark/service/service_diff" - + "github.com/APIParkLab/APIPark/module/release/dto" serviceDiff "github.com/APIParkLab/APIPark/module/service-diff" "github.com/APIParkLab/APIPark/service/api" @@ -33,6 +35,8 @@ type imlReleaseModule struct { projectDiffModule serviceDiff.IServiceDiffModule `autowired:""` releaseService release.IReleaseService `autowired:""` apiService api.IAPIService `autowired:""` + apiDocService api_doc.IAPIDocService `autowired:""` + serviceDocService service_doc.IDocService `autowired:""` upstreamService upstream.IUpstreamService `autowired:""` publishService publish.IPublishService `autowired:""` transaction store.ITransaction `autowired:""` @@ -41,7 +45,7 @@ type imlReleaseModule struct { } func (m *imlReleaseModule) Create(ctx context.Context, serviceId string, input *dto.CreateInput) (string, error) { - + proInfo, err := m.projectService.Check(ctx, serviceId, projectRuleMustServer) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { @@ -53,8 +57,8 @@ func (m *imlReleaseModule) Create(ctx context.Context, serviceId string, input * if err != nil || len(clusters) == 0 { return "", fmt.Errorf("cluster not set:%w", err) } - - apis, err := m.apiService.ListForService(ctx, proInfo.Id) + + apis, err := m.apiService.ListInfoForService(ctx, proInfo.Id) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return "", errors.New("api not found") @@ -64,7 +68,7 @@ func (m *imlReleaseModule) Create(ctx context.Context, serviceId string, input * if len(apis) == 0 { return "", errors.New("api not found") } - apiUUIDS := utils.SliceToSlice(apis, func(a *api.API) string { + apiUUIDS := utils.SliceToSlice(apis, func(a *api.Info) string { return a.UUID }) apiProxy, err := m.apiService.ListLatestCommitProxy(ctx, apiUUIDS...) @@ -77,16 +81,7 @@ func (m *imlReleaseModule) Create(ctx context.Context, serviceId string, input * if len(apis) != len(apiProxy) { return "", errors.New("api or document not found") } - apiDocs, err := m.apiService.ListLatestCommitDocument(ctx, apiUUIDS...) - if err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { - return "", errors.New("api config or document not found") - } - return "", err - } - if len(apis) != len(apiDocs) { - return "", errors.New("api or document not found") - } + upstreams, err := m.upstreamService.ListLatestCommit(ctx, serviceId) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { @@ -94,13 +89,11 @@ func (m *imlReleaseModule) Create(ctx context.Context, serviceId string, input * } return "", err } - + apiProxyCommits := utils.SliceToMapO(apiProxy, func(c *commit.Commit[api.Proxy]) (string, string) { return c.Target, c.UUID }) - apiDocumentCommits := utils.SliceToMapO(apiDocs, func(c *commit.Commit[api.Document]) (string, string) { - return c.Target, c.UUID - }) + upstreamCommits := utils.SliceToMapArray(upstreams, func(c *commit.Commit[upstream.Config]) string { return c.Target }) @@ -109,15 +102,62 @@ func (m *imlReleaseModule) Create(ctx context.Context, serviceId string, input * return c.Key, c.UUID }) }) - if !m.releaseService.Completeness(utils.SliceToSlice(clusters, func(s *cluster.Cluster) string { - return s.Uuid - }), apiUUIDS, apiProxy, apiDocs, upstreams) { - return "", errors.New("completeness check failed") - } - newRelease, err := m.releaseService.CreateRelease(ctx, serviceId, input.Version, input.Remark, apiProxyCommits, apiDocumentCommits, upstreamCommitsForUKC) + + var newRelease *release.Release + err = m.transaction.Transaction(ctx, func(ctx context.Context) error { + for _, a := range apis { + err = m.apiService.SaveRequest(ctx, a.UUID, &api.Request{ + Path: a.Path, + Methods: a.Methods, + Protocols: a.Protocols, + Match: a.Match, + Disable: a.Disable, + }) + if err != nil { + return err + } + } + requestCommits, err := m.apiService.ListLatestCommitRequest(ctx, apiUUIDS...) + if err != nil { + return err + } + + doc, err := m.apiDocService.GetDoc(ctx, serviceId) + if err != nil { + return err + } + err = m.apiDocService.CommitDoc(ctx, serviceId, doc) + if err != nil { + return err + } + docCommit, err := m.apiDocService.LatestDocCommit(ctx, serviceId) + if err != nil { + return err + } + serviceDoc, err := m.serviceDocService.Get(ctx, serviceId) + if err != nil { + return err + } + err = m.serviceDocService.CommitDoc(ctx, serviceId, serviceDoc) + if err != nil { + return err + } + serviceDocCommit, err := m.serviceDocService.LatestDocCommit(ctx, serviceId) + if !m.releaseService.Completeness(utils.SliceToSlice(clusters, func(s *cluster.Cluster) string { + return s.Uuid + }), apiUUIDS, requestCommits, apiProxy, upstreams) { + return errors.New("completeness check failed") + } + requestCommitMap := utils.SliceToMapO(requestCommits, func(c *commit.Commit[api.Request]) (string, string) { + return c.Target, c.UUID + }) + newRelease, err = m.releaseService.CreateRelease(ctx, serviceId, input.Version, input.Remark, requestCommitMap, apiProxyCommits, docCommit.UUID, serviceDocCommit.UUID, upstreamCommitsForUKC) + return err + }) if err != nil { return "", err } + return newRelease.UUID, err } @@ -169,7 +209,7 @@ func (m *imlReleaseModule) List(ctx context.Context, project string) ([]*dto.Rel if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { return nil, err } - + releaseIds := utils.SliceToSlice(list, func(s *release.Release) string { return s.UUID }) @@ -180,9 +220,9 @@ func (m *imlReleaseModule) List(ctx context.Context, project string) ([]*dto.Rel flowMap := utils.SliceToMap(flows, func(s *publish.Publish) string { return s.Release }) - + return utils.SliceToSlice(list, func(s *release.Release) *dto.Release { - + r := &dto.Release{ Id: s.UUID, Service: auto.UUID(s.Service), @@ -194,7 +234,7 @@ func (m *imlReleaseModule) List(ctx context.Context, project string) ([]*dto.Rel Creator: auto.UUID(s.Creator), CreateTime: auto.TimeLabel(s.CreateAt), } - + if running != nil && running.UUID == s.UUID { r.Status = dto.StatusRunning r.CanRollback = true @@ -203,12 +243,12 @@ func (m *imlReleaseModule) List(ctx context.Context, project string) ([]*dto.Rel flow, has := flowMap[s.UUID] if has { r.FlowId = flow.Id - + if flow.Status == publish.StatusApply { r.Status = dto.StatusApply r.CanDelete = false } else if flow.Status == publish.StatusAccept { - + r.Status = dto.StatusAccept r.CanDelete = false } else if flow.Status == publish.StatusPublishError { @@ -254,7 +294,7 @@ func (m *imlReleaseModule) Delete(ctx context.Context, project string, id string } return m.releaseService.DeleteRelease(ctx, id) }) - + } func (m *imlReleaseModule) Preview(ctx context.Context, project string) (*dto.Release, *service_diff.Diff, bool, error) { @@ -266,11 +306,11 @@ func (m *imlReleaseModule) Preview(ctx context.Context, project string) (*dto.Re if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { return nil, nil, false, err } - + if running == nil { running = new(release.Release) } - + diff, completeness, err := m.projectDiffModule.DiffForLatest(ctx, project, running.UUID) if err != nil { return nil, nil, false, err @@ -286,5 +326,5 @@ func (m *imlReleaseModule) Preview(ctx context.Context, project string) (*dto.Re CanDelete: false, CanRollback: false, }, diff, completeness, nil - + } diff --git a/module/api/dto/input.go b/module/router/dto/input.go similarity index 54% rename from module/api/dto/input.go rename to module/router/dto/input.go index 260bff2a..f281e1e2 100644 --- a/module/api/dto/input.go +++ b/module/router/dto/input.go @@ -1,12 +1,11 @@ -package api_dto +package router_dto import ( - "encoding/json" "errors" "fmt" "github.com/eolinker/go-common/utils" "strings" - + "github.com/APIParkLab/APIPark/service/api" ) @@ -20,19 +19,19 @@ var validMethods = map[string]struct{}{ "OPTIONS": {}, } -type CreateApi struct { +type Create struct { Id string `json:"id"` - Name string `json:"name"` Path string `json:"path"` - Method string `json:"method"` + Methods []string `json:"methods"` Description string `json:"description"` + Protocols []string `json:"protocols"` MatchRules []Match `json:"match"` Proxy *InputProxy `json:"proxy"` + Disable bool `json:"disable"` } type InputProxy struct { - Path string `json:"path"` - //Upstream string `json:"upstream" aocheck:"upstream"` + Path string `json:"path"` Timeout int `json:"timeout"` Retry int `json:"retry"` Headers []*Header `json:"headers"` @@ -46,44 +45,28 @@ type Match struct { Pattern string `json:"pattern"` } -func (a *CreateApi) Validate() error { +func (a *Create) Validate() error { if a.Id == "" { return errors.New("id is null") } - if a.Name == "" { - return errors.New("name is null") - } a.Path = fmt.Sprintf("/%s", strings.TrimPrefix(a.Path, "/")) - a.Method = strings.ToUpper(a.Method) - if _, ok := validMethods[a.Method]; !ok { - return fmt.Errorf("method(%s) is invalid", a.Method) + for _, method := range a.Methods { + if _, ok := validMethods[method]; !ok { + return fmt.Errorf("method(%s) is invalid", method) + } } + return nil } -func (a *CreateApi) ToServiceRouter() *api.Router { - router := &api.Router{ - Method: a.Method, - Path: a.Path, - } - for _, match := range a.MatchRules { - router.MatchRules = append(router.MatchRules, &api.Match{ - Position: match.Position, - MatchType: match.MatchType, - Key: match.Key, - Pattern: match.Pattern, - }) - } - return router -} - -type EditApi struct { - Info struct { - Name *string `json:"name"` - Description *string `json:"description"` - } `json:"info"` - Proxy *InputProxy `json:"proxy"` - Doc *map[string]interface{} `json:"doc"` +type Edit struct { + Description *string `json:"description"` + Proxy *InputProxy `json:"proxy"` + Path *string `json:"path"` + Methods *[]string `json:"methods"` + Protocols *[]string `json:"protocols"` + MatchRules *[]Match `json:"match"` + Disable *bool `json:"disable"` } func ToServiceProxy(proxy *InputProxy) *api.Proxy { @@ -97,10 +80,9 @@ func ToServiceProxy(proxy *InputProxy) *api.Proxy { Opt: h.Opt, } }) - + return &api.Proxy{ - Path: proxy.Path, - //Upstream: proxy.Upstream, + Path: proxy.Path, Timeout: proxy.Timeout, Retry: proxy.Retry, Extends: proxy.Extends, @@ -109,19 +91,10 @@ func ToServiceProxy(proxy *InputProxy) *api.Proxy { } } -func ToServiceDocument(doc map[string]interface{}) *api.Document { - if doc == nil { - return &api.Document{ - Content: "{}", - } - } - content, _ := json.Marshal(doc) - - return &api.Document{ - Content: string(content), - } -} - type ListInput struct { Projects []string `json:"projects"` } + +type UpdateDoc struct { + Content string `json:"content"` +} diff --git a/module/api/dto/output.go b/module/router/dto/output.go similarity index 53% rename from module/api/dto/output.go rename to module/router/dto/output.go index 1787e039..53988c2b 100644 --- a/module/api/dto/output.go +++ b/module/router/dto/output.go @@ -1,51 +1,54 @@ -package api_dto +package router_dto import ( "encoding/json" - + "github.com/eolinker/go-common/utils" - + "github.com/APIParkLab/APIPark/service/api" "github.com/eolinker/go-common/auto" ) -type ApiItem struct { - Id string `json:"id"` - Name string `json:"name"` - Method string `json:"method"` - Path string `json:"request_path"` - Creator auto.Label `json:"creator" aolabel:"user"` - Updater auto.Label `json:"updater" aolabel:"user"` - CreateTime auto.TimeLabel `json:"create_time"` - UpdateTime auto.TimeLabel `json:"update_time"` - CanDelete bool `json:"can_delete"` +type Item struct { + Id string `json:"id"` + Methods []string `json:"methods"` + Protocols []string `json:"protocols"` + Path string `json:"request_path"` + Description string `json:"description"` + Disable bool `json:"disable"` + Creator auto.Label `json:"creator" aolabel:"user"` + Updater auto.Label `json:"updater" aolabel:"user"` + CreateTime auto.TimeLabel `json:"create_time"` + UpdateTime auto.TimeLabel `json:"update_time"` + CanDelete bool `json:"can_delete"` } -type ApiSimpleItem struct { - Id string `json:"id"` - Name string `json:"name"` - Method string `json:"method"` - Path string `json:"request_path"` +type SimpleItem struct { + Id string `json:"id"` + Methods []string `json:"methods"` + Path string `json:"request_path"` } -type ApiDetail struct { - ApiSimpleDetail - Proxy *Proxy `json:"proxy"` - Doc map[string]interface{} `json:"doc"` +type Detail struct { + SimpleDetail + Proxy *Proxy `json:"proxy"` + Protocols []string `json:"protocols"` + Disable bool `json:"disable"` + //Doc map[string]interface{} `json:"doc"` } -func GenApiSimpleDetail(api *api.Info) *ApiSimpleDetail { +func GenSimpleDetail(api *api.Info) *SimpleDetail { match := make([]Match, 0) if api.Match == "" { api.Match = "[]" } json.Unmarshal([]byte(api.Match), &match) - - return &ApiSimpleDetail{ + + return &SimpleDetail{ Id: api.UUID, Name: api.Name, Description: api.Description, - Method: api.Method, + Methods: api.Methods, Path: api.Path, MatchRules: match, Creator: auto.UUID(api.Creator), @@ -55,12 +58,13 @@ func GenApiSimpleDetail(api *api.Info) *ApiSimpleDetail { } } -type ApiSimpleDetail struct { +type SimpleDetail struct { Id string `json:"id"` Name string `json:"name"` Description string `json:"description"` - Method string `json:"method"` + Methods []string `json:"methods"` Path string `json:"path"` + Protocols []string `json:"protocols"` MatchRules []Match `json:"match"` Creator auto.Label `json:"creator" aolabel:"user"` Updater auto.Label `json:"updater" aolabel:"user"` @@ -68,21 +72,11 @@ type ApiSimpleDetail struct { UpdateTime auto.TimeLabel `json:"update_time"` } -type ApiDocDetail struct { - ApiSimpleDetail - Doc map[string]interface{} `json:"doc"` -} - -type ApiProxyDetail struct { - ApiSimpleDetail - Proxy *Proxy `json:"proxy"` -} - func FromServiceProxy(proxy *api.Proxy) *Proxy { if proxy == nil { return nil } - + return &Proxy{ Path: proxy.Path, Timeout: proxy.Timeout, @@ -113,3 +107,16 @@ type Header struct { Value string `json:"value"` Opt string `json:"opt"` } + +type Export struct { + Id string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + Method []string `json:"method"` + Path string `json:"path"` + MatchRules []Match `json:"match"` + Service string `json:"service"` + Team string `json:"team"` + Proxy *Proxy `json:"proxy"` + Doc map[string]interface{} `json:"doc"` +} diff --git a/module/router/iml.go b/module/router/iml.go new file mode 100644 index 00000000..5479ac6a --- /dev/null +++ b/module/router/iml.go @@ -0,0 +1,394 @@ +package router + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "github.com/APIParkLab/APIPark/service/universally/commit" + "strings" + + "github.com/APIParkLab/APIPark/service/service" + "github.com/APIParkLab/APIPark/service/upstream" + + "gorm.io/gorm" + + "github.com/APIParkLab/APIPark/service/team" + + "github.com/google/uuid" + + "github.com/eolinker/go-common/auto" + "github.com/eolinker/go-common/utils" + + "github.com/eolinker/go-common/store" + + "github.com/APIParkLab/APIPark/service/api" + + router_dto "github.com/APIParkLab/APIPark/module/router/dto" +) + +var ( + _ IRouterModule = (*imlRouterModule)(nil) + _ IExportRouterModule = (*imlRouterModule)(nil) +) +var ( + asServer = map[string]bool{ + "as_server": true, + } +) + +type imlRouterModule struct { + teamService team.ITeamService `autowired:""` + serviceService service.IServiceService `autowired:""` + apiService api.IAPIService `autowired:""` + upstreamService upstream.IUpstreamService `autowired:""` + transaction store.ITransaction `autowired:""` +} + +func (i *imlRouterModule) ExportAll(ctx context.Context) ([]*router_dto.Export, error) { + + apiList, err := i.apiService.ListInfo(ctx) + if err != nil { + return nil, err + } + apiIds := utils.SliceToSlice(apiList, func(a *api.Info) string { + return a.UUID + }) + proxyCommits, err := i.apiService.ListLatestCommitProxy(ctx, apiIds...) + if err != nil { + return nil, err + } + proxyCommitMap := utils.SliceToMap(proxyCommits, func(c *commit.Commit[api.Proxy]) string { + return c.Target + }) + + return utils.SliceToSlice(apiList, func(a *api.Info) *router_dto.Export { + match := make([]router_dto.Match, 0) + if a.Match == "" { + a.Match = "[]" + } + json.Unmarshal([]byte(a.Match), &match) + info := &router_dto.Export{ + Id: a.UUID, + Name: a.Name, + Description: a.Description, + Path: a.Path, + MatchRules: match, + Service: a.Service, + Team: a.Team, + } + if v, ok := proxyCommitMap[a.UUID]; ok { + info.Proxy = router_dto.FromServiceProxy(v.Data) + } + + return info + }), nil +} + +func (i *imlRouterModule) SimpleList(ctx context.Context, serviceId string) ([]*router_dto.SimpleItem, error) { + + list, err := i.apiService.ListForService(ctx, serviceId) + apiInfos, err := i.apiService.ListInfo(ctx, utils.SliceToSlice(list, func(s *api.API) string { + return s.UUID + })...) + if err != nil { + return nil, err + } + + out := utils.SliceToSlice(apiInfos, func(item *api.Info) *router_dto.SimpleItem { + return &router_dto.SimpleItem{ + Id: item.UUID, + Path: item.Path, + } + }) + return out, nil +} + +func (i *imlRouterModule) Detail(ctx context.Context, serviceId string, apiId string) (*router_dto.Detail, error) { + _, err := i.serviceService.Check(ctx, serviceId, asServer) + if err != nil { + return nil, err + } + + detail, err := i.apiService.GetInfo(ctx, apiId) + if err != nil { + return nil, err + } + protocols := []string{"HTTP", "HTTPS"} + if len(detail.Protocols) > 0 { + protocols = detail.Protocols + } + + apiDetail := &router_dto.Detail{ + SimpleDetail: *router_dto.GenSimpleDetail(detail), + Protocols: protocols, + Disable: detail.Disable, + } + proxy, err := i.apiService.LatestProxy(ctx, apiId) + if err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + return nil, err + } + } + if proxy != nil { + apiDetail.Proxy = router_dto.FromServiceProxy(proxy.Data) + } + + return apiDetail, nil +} + +func (i *imlRouterModule) SimpleDetail(ctx context.Context, serviceId string, apiId string) (*router_dto.SimpleDetail, error) { + _, err := i.serviceService.Check(ctx, serviceId, asServer) + if err != nil { + return nil, err + } + + detail, err := i.apiService.GetInfo(ctx, apiId) + if err != nil { + return nil, err + } + + return router_dto.GenSimpleDetail(detail), nil +} + +func (i *imlRouterModule) Search(ctx context.Context, keyword string, serviceId string) ([]*router_dto.Item, error) { + _, err := i.serviceService.Check(ctx, serviceId, asServer) + if err != nil { + return nil, err + } + + list, err := i.apiService.Search(ctx, keyword, map[string]interface{}{ + "service": serviceId, + }) + if err != nil { + return nil, err + } + apiInfos, err := i.apiService.ListInfo(ctx, utils.SliceToSlice(list, func(s *api.API) string { + return s.UUID + })...) + if err != nil { + return nil, err + } + utils.Sort(apiInfos, func(a, b *api.Info) bool { + return a.UpdateAt.After(b.UpdateAt) + }) + out := utils.SliceToSlice(apiInfos, func(item *api.Info) *router_dto.Item { + protocols := []string{"HTTP", "HTTPS"} + if len(item.Protocols) > 0 { + protocols = item.Protocols + } + return &router_dto.Item{ + Id: item.UUID, + Methods: item.Methods, + Protocols: protocols, + Path: item.Path, + Description: item.Description, + Disable: item.Disable, + Creator: auto.UUID(item.Creator), + Updater: auto.UUID(item.Updater), + CreateTime: auto.TimeLabel(item.CreateAt), + UpdateTime: auto.TimeLabel(item.UpdateAt), + CanDelete: true, + } + }) + + return out, nil +} + +func (i *imlRouterModule) SimpleSearch(ctx context.Context, keyword string, serviceId string) ([]*router_dto.SimpleItem, error) { + _, err := i.serviceService.Check(ctx, serviceId, asServer) + if err != nil { + return nil, err + } + + list, err := i.apiService.Search(ctx, keyword, map[string]interface{}{ + "service": serviceId, + }) + if err != nil { + return nil, err + } + apiInfos, err := i.apiService.ListInfo(ctx, utils.SliceToSlice(list, func(s *api.API) string { + return s.UUID + })...) + if err != nil { + return nil, err + } + out := utils.SliceToSlice(apiInfos, func(item *api.Info) *router_dto.SimpleItem { + return &router_dto.SimpleItem{ + Id: item.UUID, + Methods: item.Methods, + Path: item.Path, + } + }) + return out, nil +} + +func (i *imlRouterModule) Create(ctx context.Context, serviceId string, dto *router_dto.Create) (*router_dto.SimpleDetail, error) { + info, err := i.serviceService.Check(ctx, serviceId, asServer) + if err != nil { + return nil, err + } + prefix, err := i.Prefix(ctx, serviceId) + if err != nil { + return nil, err + } + err = i.transaction.Transaction(ctx, func(ctx context.Context) error { + if dto.Id == "" { + dto.Id = uuid.New().String() + } + err = dto.Validate() + if err != nil { + return err + } + + path := fmt.Sprintf("%s%s", prefix, dto.Path) + + err = i.apiService.Exist(ctx, "", &api.Exist{Path: dto.Path, Methods: dto.Methods}) + if err != nil { + return err + } + + proxy := router_dto.ToServiceProxy(dto.Proxy) + err = i.apiService.SaveProxy(ctx, dto.Id, proxy) + if err != nil { + return err + } + + match, _ := json.Marshal(dto.MatchRules) + return i.apiService.Create(ctx, &api.Create{ + UUID: dto.Id, + Description: dto.Description, + Service: serviceId, + Team: info.Team, + Methods: dto.Methods, + Path: path, + Match: string(match), + }) + }) + if err != nil { + return nil, err + } + return i.SimpleDetail(ctx, serviceId, dto.Id) +} + +func (i *imlRouterModule) Edit(ctx context.Context, serviceId string, apiId string, dto *router_dto.Edit) (*router_dto.SimpleDetail, error) { + _, err := i.serviceService.Check(ctx, serviceId, asServer) + if err != nil { + return nil, err + } + + err = i.transaction.Transaction(ctx, func(ctx context.Context) error { + if dto.Proxy != nil { + err = i.apiService.SaveProxy(ctx, apiId, router_dto.ToServiceProxy(dto.Proxy)) + if err != nil { + return err + } + } + var match *string + if dto.MatchRules != nil { + ml, _ := json.Marshal(dto.MatchRules) + m := string(ml) + match = &m + } + err = i.apiService.Save(ctx, apiId, &api.Edit{ + Description: dto.Description, + Methods: dto.Methods, + Protocols: dto.Protocols, + Disable: dto.Disable, + Path: dto.Path, + Match: match, + }) + if err != nil { + return err + } + + return nil + + }) + if err != nil { + return nil, err + } + return i.SimpleDetail(ctx, serviceId, apiId) +} + +func (i *imlRouterModule) Delete(ctx context.Context, serviceId string, apiId string) error { + _, err := i.serviceService.Check(ctx, serviceId, asServer) + if err != nil { + return err + } + return i.apiService.Delete(ctx, apiId) +} + +//func (i *imlRouterModule) Copy(ctx context.Context, serviceId string, apiId string, dto *router_dto.Create) (*router_dto.SimpleDetail, error) { +// info, err := i.serviceService.Check(ctx, serviceId, asServer) +// if err != nil { +// return nil, err +// } +// oldApi, err := i.apiService.Get(ctx, apiId) +// if err != nil { +// return nil, err +// } +// prefix, err := i.Prefix(ctx, serviceId) +// if err != nil { +// return nil, err +// } +// err = i.transaction.Transaction(ctx, func(ctx context.Context) error { +// if dto.Id == "" { +// dto.Id = uuid.New().String() +// } +// err = dto.Validate() +// if err != nil { +// return err +// } +// +// path := fmt.Sprintf("%s/%s", strings.TrimSuffix(prefix, "/"), strings.TrimPrefix(dto.Path, "/")) +// err = i.apiService.Exist(ctx, serviceId, &api.Exist{Path: path, Methods: dto.Methods}) +// if err != nil { +// return err +// } +// +// proxy, err := i.apiService.LatestProxy(ctx, oldApi.UUID) +// if err != nil { +// if !errors.Is(err, gorm.ErrRecordNotFound) { +// return err +// } +// } +// //upstreamId := "" +// if proxy != nil { +// err = i.apiService.SaveProxy(ctx, dto.Id, proxy.Data) +// if err != nil { +// return err +// } +// } +// +// match, _ := json.Marshal(dto.MatchRules) +// return i.apiService.Create(ctx, &api.Create{ +// UUID: dto.Id, +// Name: dto.Name, +// Service: serviceId, +// Team: info.Team, +// Methods: dto.Methods, +// Path: path, +// Match: string(match), +// }) +// +// }) +// if err != nil { +// return nil, err +// } +// return i.SimpleDetail(ctx, serviceId, dto.Id) +//} + +func (i *imlRouterModule) Prefix(ctx context.Context, serviceId string) (string, error) { + pInfo, err := i.serviceService.Check(ctx, serviceId, asServer) + if err != nil { + return "", err + } + + if pInfo.Prefix != "" { + if pInfo.Prefix[0] != '/' { + pInfo.Prefix = fmt.Sprintf("/%s", strings.TrimSuffix(pInfo.Prefix, "/")) + } + } + return strings.TrimSuffix(pInfo.Prefix, "/"), nil +} diff --git a/module/router/router.go b/module/router/router.go new file mode 100644 index 00000000..1a43b958 --- /dev/null +++ b/module/router/router.go @@ -0,0 +1,48 @@ +package router + +import ( + "context" + "github.com/APIParkLab/APIPark/module/system" + "reflect" + + "github.com/eolinker/go-common/autowire" + + router_dto "github.com/APIParkLab/APIPark/module/router/dto" +) + +type IRouterModule interface { + // Detail 获取API详情 + Detail(ctx context.Context, serviceId string, apiId string) (*router_dto.Detail, error) + // SimpleDetail 获取API简要详情 + SimpleDetail(ctx context.Context, serviceId string, apiId string) (*router_dto.SimpleDetail, error) + // Search 获取API列表 + Search(ctx context.Context, keyword string, serviceId string) ([]*router_dto.Item, error) + // SimpleSearch 获取API简要列表 + SimpleSearch(ctx context.Context, keyword string, serviceId string) ([]*router_dto.SimpleItem, error) + SimpleList(ctx context.Context, serviceId string) ([]*router_dto.SimpleItem, error) + // Create 创建API + Create(ctx context.Context, serviceId string, dto *router_dto.Create) (*router_dto.SimpleDetail, error) + // Edit 编辑API + Edit(ctx context.Context, serviceId string, apiId string, dto *router_dto.Edit) (*router_dto.SimpleDetail, error) + // Delete 删除API + Delete(ctx context.Context, serviceId string, apiId string) error + // Prefix 获取API前缀 + Prefix(ctx context.Context, serviceId string) (string, error) + + //ExportAll(ctx context.Context) ([]*router_dto.Export, error) +} + +type IExportRouterModule interface { + system.IExportModule[router_dto.Export] +} + +func init() { + apiModule := new(imlRouterModule) + autowire.Auto[IRouterModule](func() reflect.Value { + return reflect.ValueOf(apiModule) + }) + + autowire.Auto[IExportRouterModule](func() reflect.Value { + return reflect.ValueOf(apiModule) + }) +} diff --git a/module/service-diff/iml.go b/module/service-diff/iml.go index 12248178..e4612394 100644 --- a/module/service-diff/iml.go +++ b/module/service-diff/iml.go @@ -4,19 +4,19 @@ import ( "context" "errors" "fmt" - "github.com/APIParkLab/APIPark/service/api" + api_doc "github.com/APIParkLab/APIPark/service/api-doc" "github.com/APIParkLab/APIPark/service/cluster" "github.com/APIParkLab/APIPark/service/release" "github.com/APIParkLab/APIPark/service/service_diff" "github.com/APIParkLab/APIPark/service/universally/commit" "github.com/APIParkLab/APIPark/service/upstream" - "github.com/eolinker/go-common/auto" "github.com/eolinker/go-common/utils" ) type imlServiceDiff struct { apiService api.IAPIService `autowired:""` + apiDocService api_doc.IAPIDocService `autowired:""` upstreamService upstream.IUpstreamService `autowired:""` releaseService release.IReleaseService `autowired:""` clusterService cluster.IClusterService `autowired:""` @@ -89,11 +89,26 @@ func (m *imlServiceDiff) DiffForLatest(ctx context.Context, serviceId string, ba if err != nil { return nil, false, err } + request := make([]*commit.Commit[api.Request], 0, len(apiInfos)) + for _, apiInfo := range apiInfos { + request = append(request, &commit.Commit[api.Request]{ + Target: apiInfo.UUID, + Key: "request", + Data: &api.Request{ + Path: apiInfo.Path, + Methods: apiInfo.Methods, + Protocols: apiInfo.Protocols, + Match: apiInfo.Match, + Disable: apiInfo.Disable, + }, + }) + + } proxy, err := m.apiService.ListLatestCommitProxy(ctx, apiIds...) if err != nil { return nil, false, fmt.Errorf("diff for api commit %v", err) } - documents, err := m.apiService.ListLatestCommitDocument(ctx, apiIds...) + apiDocCommits, err := m.apiDocService.ListLatestDocCommit(ctx, serviceId) if err != nil { return nil, false, err } @@ -108,11 +123,11 @@ func (m *imlServiceDiff) DiffForLatest(ctx context.Context, serviceId string, ba return nil, false, err } target := &projectInfo{ - id: serviceId, - apis: apiInfos, - apiCommits: proxy, - apiDocs: documents, - upstreamCommits: upstreamCommits, + id: serviceId, + apiRequestCommits: request, + apiProxyCommits: proxy, + apiDocCommits: apiDocCommits, + upstreamCommits: upstreamCommits, } clusters, err := m.clusterService.List(ctx) if err != nil { @@ -128,16 +143,11 @@ func (m *imlServiceDiff) getReleaseInfo(ctx context.Context, releaseId string) ( if err != nil { return nil, err } - - apiIds := utils.SliceToSlice(commits, func(i *release.ProjectCommits) string { + apiRequestCommitIds := utils.SliceToSlice(commits, func(i *release.ProjectCommits) string { return i.Target }, func(c *release.ProjectCommits) bool { - return c.Type == release.CommitApiProxy || c.Type == release.CommitApiDocument + return c.Type == release.CommitApiRequest }) - apiInfos, err := m.apiService.ListInfo(ctx, apiIds...) - if err != nil { - return nil, err - } apiProxyCommitIds := utils.SliceToSlice(commits, func(i *release.ProjectCommits) string { return i.Commit }, func(c *release.ProjectCommits) bool { @@ -153,56 +163,71 @@ func (m *imlServiceDiff) getReleaseInfo(ctx context.Context, releaseId string) ( }, func(c *release.ProjectCommits) bool { return c.Type == release.CommitUpstream }) - proxyCommits, err := m.apiService.ListProxyCommit(ctx, apiProxyCommitIds...) - if err != nil { - return nil, err + var requestCommits []*commit.Commit[api.Request] + var proxyCommits []*commit.Commit[api.Proxy] + var documentCommits []*commit.Commit[api_doc.DocCommit] + if len(apiRequestCommitIds) > 0 { + requestCommits, err = m.apiService.ListRequestCommit(ctx, apiRequestCommitIds...) + if err != nil { + return nil, err + } } - documentCommits, err := m.apiService.ListDocumentCommit(ctx, apiDocumentCommitIds...) - if err != nil { - return nil, err + if len(apiProxyCommitIds) > 0 { + proxyCommits, err = m.apiService.ListProxyCommit(ctx, apiProxyCommitIds...) + if err != nil { + return nil, err + } } + if len(apiDocumentCommitIds) > 0 { + documentCommits, err = m.apiDocService.ListDocCommit(ctx, apiDocumentCommitIds...) + if err != nil { + return nil, err + } + } + upstreamCommits, err := m.upstreamService.ListCommit(ctx, upstreamCommitIds...) if err != nil { return nil, err } return &projectInfo{ - apis: apiInfos, - apiCommits: proxyCommits, - apiDocs: documentCommits, - upstreamCommits: upstreamCommits, + apiRequestCommits: requestCommits, + apiProxyCommits: proxyCommits, + apiDocCommits: documentCommits, + upstreamCommits: upstreamCommits, }, nil } func (m *imlServiceDiff) diff(partitions []string, base, target *projectInfo) *service_diff.Diff { out := &service_diff.Diff{ Apis: nil, Upstreams: nil, - //Clusters: partitions, } - baseApis := utils.NewSet(utils.SliceToSlice(base.apis, func(i *api.Info) string { - return i.UUID + baseApis := utils.NewSet(utils.SliceToSlice(base.apiRequestCommits, func(i *commit.Commit[api.Request]) string { + return i.Target })...) - baseApiProxy := utils.SliceToMap(base.apiCommits, func(i *commit.Commit[api.Proxy]) string { + baseApiProxy := utils.SliceToMap(base.apiProxyCommits, func(i *commit.Commit[api.Proxy]) string { return i.Target }) - baseAPIDoc := utils.SliceToMap(base.apiDocs, func(i *commit.Commit[api.Document]) string { + baseAPIDoc := utils.SliceToMap(base.apiDocCommits, func(i *commit.Commit[api_doc.DocCommit]) string { return i.Target }) - targetApiProxy := utils.SliceToMap(target.apiCommits, func(i *commit.Commit[api.Proxy]) string { + targetApiProxy := utils.SliceToMap(target.apiProxyCommits, func(i *commit.Commit[api.Proxy]) string { return i.Target }) - targetAPIDoc := utils.SliceToMap(target.apiDocs, func(i *commit.Commit[api.Document]) string { + targetAPIDoc := utils.SliceToMap(target.apiDocCommits, func(i *commit.Commit[api_doc.DocCommit]) string { return i.Target }) - for _, apiInfo := range target.apis { - apiId := apiInfo.UUID + for _, rc := range target.apiRequestCommits { + apiId := rc.Target a := &service_diff.ApiDiff{ - APi: apiInfo.UUID, - Name: apiInfo.Name, - Method: apiInfo.Method, - Path: apiInfo.Path, - Status: service_diff.Status{}, + APi: rc.Target, + Method: rc.Data.Methods, + Protocol: rc.Data.Protocols, + Disable: false, + Path: rc.Data.Path, + Change: 0, + Status: service_diff.Status{}, } pc, hasPc := targetApiProxy[apiId] @@ -237,15 +262,17 @@ func (m *imlServiceDiff) diff(partitions []string, base, target *projectInfo) *s baseApis.Remove(utils.SliceToSlice(out.Apis, func(i *service_diff.ApiDiff) string { return i.APi })...) - for _, apiInfo := range base.apis { - if baseApis.Has(apiInfo.UUID) { + for _, rc := range base.apiRequestCommits { + apiInfo := rc.Data + if baseApis.Has(rc.Target) { out.Apis = append(out.Apis, &service_diff.ApiDiff{ - APi: apiInfo.UUID, - Name: apiInfo.Name, - Method: apiInfo.Method, - Path: apiInfo.Path, - Status: service_diff.Status{}, - Change: service_diff.ChangeTypeDelete, + APi: rc.Target, + Method: apiInfo.Methods, + Protocol: apiInfo.Protocols, + Disable: apiInfo.Disable, + Path: apiInfo.Path, + Change: service_diff.ChangeTypeDelete, + Status: service_diff.Status{}, }) } @@ -262,10 +289,9 @@ func (m *imlServiceDiff) diff(partitions []string, base, target *projectInfo) *s key := fmt.Sprintf("%s-%s", target.id, partitionId) o := &service_diff.UpstreamDiff{ Upstream: target.id, - //Cluster: partitionId, - Data: nil, - Change: service_diff.ChangeTypeNone, - Status: 0, + Data: nil, + Change: service_diff.ChangeTypeNone, + Status: 0, } out.Upstreams = append(out.Upstreams, o) bu, hasBu := baseUpstreamMap[key] @@ -300,14 +326,14 @@ func (m *imlServiceDiff) Out(ctx context.Context, diff *service_diff.Diff) (*Dif } out := &DiffOut{} - out.Apis = utils.SliceToSlice(diff.Apis, func(i *service_diff.ApiDiff) *ApiDiffOut { - return &ApiDiffOut{ - Api: auto.UUID(i.APi), - Name: i.Name, - Method: i.Method, - Path: i.Path, - Change: i.Change, - Status: i.Status, + out.Routers = utils.SliceToSlice(diff.Apis, func(i *service_diff.ApiDiff) *RouterDiffOut { + return &RouterDiffOut{ + Methods: i.Method, + Path: i.Path, + Change: i.Change, + Status: i.Status, + Protocols: i.Protocol, + Disable: i.Disable, } }) diff --git a/module/service-diff/out.go b/module/service-diff/out.go index 1c204c4e..16dd6841 100644 --- a/module/service-diff/out.go +++ b/module/service-diff/out.go @@ -2,25 +2,25 @@ package service_diff import ( "github.com/APIParkLab/APIPark/service/api" + api_doc "github.com/APIParkLab/APIPark/service/api-doc" "github.com/APIParkLab/APIPark/service/service_diff" "github.com/APIParkLab/APIPark/service/universally/commit" "github.com/APIParkLab/APIPark/service/upstream" - "github.com/eolinker/go-common/auto" ) type DiffOut struct { - Apis []*ApiDiffOut `json:"apis"` + Routers []*RouterDiffOut `json:"routers"` Upstreams []*UpstreamDiffOut `json:"upstreams"` } -type ApiDiffOut struct { - Api auto.Label `json:"api,omitempty" aolabel:"api"` - Name string `json:"name,omitempty"` - Method string `json:"method,omitempty"` - Path string `json:"path,omitempty"` - //Upstream auto.Label `json:"upstream,omitempty" aolabel:"upstream"` - Change service_diff.ChangeType `json:"change,omitempty"` - Status service_diff.Status `json:"status,omitempty"` +type RouterDiffOut struct { + Methods []string `json:"methods,omitempty"` + Protocols []string `json:"protocols,omitempty"` + Path string `json:"path,omitempty"` + Description string `json:"description"` + Change service_diff.ChangeType `json:"change,omitempty"` + Status service_diff.Status `json:"status,omitempty"` + Disable bool `json:"disable,omitempty"` } type UpstreamDiffOut struct { Change service_diff.ChangeType `json:"change,omitempty"` @@ -29,38 +29,11 @@ type UpstreamDiffOut struct { Addr []string `json:"addr,omitempty"` } -// -//func CreateOut(d *project_diff.Diff) *DiffOut { -// if d == nil { -// return nil -// } -// return &DiffOut{ -// Apis: utils.SliceToSlice(d.Apis, func(s *project_diff.ApiDiff) *ApiDiffOut { -// return &ApiDiffOut{ -// Name: s.Name, -// Method: s.Method, -// Path: s.Path, -// Upstream: s.Upstream, -// Change: s.Change, -// } -// }), -// Upstreams: utils.SliceToSlice(d.Upstreams, func(s *project_diff.UpstreamDiff) *UpstreamDiffOut { -// return &UpstreamDiffOut{ -// Upstream: s.Name, -// Cluster: auto.UUID(s.Cluster), -// Cluster: auto.UUID(s.Cluster), -// Change: s.Change, -// Type: s.Type, -// Addr: s.Addr, -// } -// }), -// } -//} - type projectInfo struct { - id string - apis []*api.Info - apiCommits []*commit.Commit[api.Proxy] - apiDocs []*commit.Commit[api.Document] - upstreamCommits []*commit.Commit[upstream.Config] + id string + //apis []*api.Info + apiRequestCommits []*commit.Commit[api.Request] + apiProxyCommits []*commit.Commit[api.Proxy] + apiDocCommits []*commit.Commit[api_doc.DocCommit] + upstreamCommits []*commit.Commit[upstream.Config] } diff --git a/module/service/dto/input.go b/module/service/dto/input.go index 5f49366b..c75704ac 100644 --- a/module/service/dto/input.go +++ b/module/service/dto/input.go @@ -8,7 +8,7 @@ type CreateService struct { ServiceType string `json:"service_type"` Logo string `json:"logo"` Tags []string `json:"tags"` - Catalogue string `json:"catalogue" aocheck:"catalogue"` + Catalogue string `json:"catalogue"` AsApp *bool `json:"as_app"` AsServer *bool `json:"as_server"` } @@ -17,7 +17,7 @@ type EditService struct { Name *string `json:"name"` Description *string `json:"description"` ServiceType *string `json:"service_type"` - Catalogue *string `json:"catalogue" aocheck:"catalogue"` + Catalogue *string `json:"catalogue"` Logo *string `json:"logo"` Tags *[]string `json:"tags"` } diff --git a/module/service/dto/output.go b/module/service/dto/output.go index da0f248e..4e34e420 100644 --- a/module/service/dto/output.go +++ b/module/service/dto/output.go @@ -113,3 +113,23 @@ type ServiceDoc struct { Updater auto.Label `json:"updater" aolabel:"user"` UpdateTime auto.TimeLabel `json:"update_time"` } + +type ExportService struct { + Id string `json:"id"` + Name string `json:"name"` + Prefix string `json:"prefix,omitempty"` + Description string `json:"description"` + Team string `json:"team"` + ServiceType string `json:"service_type"` + Catalogue string `json:"catalogue"` + Tags []string `json:"tags"` + Logo string `json:"logo"` + Doc string `json:"doc"` +} + +type ExportApp struct { + Id string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + Team string `json:"team"` +} diff --git a/module/service/iml.go b/module/service/iml.go index 313ff39e..684a610a 100644 --- a/module/service/iml.go +++ b/module/service/iml.go @@ -4,41 +4,43 @@ import ( "context" "errors" "fmt" + api_doc "github.com/APIParkLab/APIPark/service/api-doc" "sort" "strings" - + service_tag "github.com/APIParkLab/APIPark/service/service-tag" - + service_doc "github.com/APIParkLab/APIPark/service/service-doc" - + serviceDto "github.com/APIParkLab/APIPark/module/service/dto" - + "github.com/APIParkLab/APIPark/service/tag" - + "github.com/APIParkLab/APIPark/service/service" - + "github.com/APIParkLab/APIPark/service/subscribe" "gorm.io/gorm" - + "github.com/APIParkLab/APIPark/service/api" - + "github.com/eolinker/go-common/auto" - + team_member "github.com/APIParkLab/APIPark/service/team-member" - + "github.com/eolinker/go-common/store" - + "github.com/google/uuid" - + "github.com/eolinker/go-common/utils" - + "github.com/APIParkLab/APIPark/service/team" - + service_dto "github.com/APIParkLab/APIPark/module/service/dto" ) var ( - _ IServiceModule = (*imlServiceModule)(nil) + _ IServiceModule = (*imlServiceModule)(nil) + _ IExportServiceModule = (*imlServiceModule)(nil) ) type imlServiceModule struct { @@ -49,11 +51,68 @@ type imlServiceModule struct { serviceDocService service_doc.IDocService `autowired:""` serviceTagService service_tag.ITagService `autowired:""` apiService api.IAPIService `autowired:""` + apiDocService api_doc.IAPIDocService `autowired:""` transaction store.ITransaction `autowired:""` } +func (i *imlServiceModule) ExportAll(ctx context.Context) ([]*service_dto.ExportService, error) { + services, err := i.serviceService.ServiceList(ctx) + if err != nil { + return nil, err + } + serviceIds := utils.SliceToSlice(services, func(s *service.Service) string { + return s.Id + }) + serviceTags, err := i.serviceTagService.List(ctx, serviceIds, nil) + if err != nil { + return nil, err + } + tagMap, err := i.tagService.Map(ctx) + if err != nil { + return nil, err + } + serviceTagMap := make(map[string][]string) + for _, st := range serviceTags { + if _, ok := tagMap[st.Tid]; !ok { + continue + } + if _, ok := serviceTagMap[st.Sid]; !ok { + serviceTagMap[st.Sid] = make([]string, 0) + } + serviceTagMap[st.Sid] = append(serviceTagMap[st.Sid], tagMap[st.Tid].Name) + } + + docMap, err := i.serviceDocService.Map(ctx, serviceIds...) + if err != nil { + return nil, err + } + + items := make([]*service_dto.ExportService, 0, len(services)) + for _, s := range services { + info := &service_dto.ExportService{ + Id: s.Id, + Name: s.Name, + Prefix: s.Prefix, + Description: s.Description, + Team: s.Team, + ServiceType: s.ServiceType.String(), + Catalogue: s.Catalogue, + Logo: s.Logo, + } + if v, ok := docMap[s.Id]; ok { + info.Doc = v.Doc + } + if tags, ok := serviceTagMap[s.Id]; ok { + info.Tags = tags + } + items = append(items, info) + } + return items, nil + +} + func (i *imlServiceModule) searchMyServices(ctx context.Context, teamId string, keyword string) ([]*service.Service, error) { - + userID := utils.UserId(ctx) condition := make(map[string]interface{}) condition["as_server"] = true @@ -73,7 +132,7 @@ func (i *imlServiceModule) searchMyServices(ctx context.Context, teamId string, condition["team"] = teamIds return i.serviceService.Search(ctx, keyword, condition, "update_at desc") } - + } func (i *imlServiceModule) SearchMyServices(ctx context.Context, teamId string, keyword string) ([]*service_dto.ServiceItem, error) { @@ -84,11 +143,11 @@ func (i *imlServiceModule) SearchMyServices(ctx context.Context, teamId string, serviceIds := utils.SliceToSlice(services, func(p *service.Service) string { return p.Id }) - apiCountMap, err := i.apiService.CountByGroup(ctx, "", map[string]interface{}{"service": serviceIds}, "service") + apiCountMap, err := i.apiDocService.APICountByServices(ctx, serviceIds...) if err != nil { return nil, err } - + items := make([]*service_dto.ServiceItem, 0, len(services)) for _, model := range services { if teamId != "" && model.Team != teamId { @@ -121,7 +180,7 @@ func (i *imlServiceModule) SimpleAPPS(ctx context.Context, keyword string) ([]*s Id: p.Id, Name: p.Name, Description: p.Description, - + Team: auto.UUID(p.Team), } }), nil @@ -130,15 +189,15 @@ func (i *imlServiceModule) SimpleAPPS(ctx context.Context, keyword string) ([]*s func (i *imlServiceModule) Simple(ctx context.Context, keyword string) ([]*service_dto.SimpleServiceItem, error) { w := make(map[string]interface{}) w["as_server"] = true - + services, err := i.serviceService.Search(ctx, keyword, w) if err != nil { return nil, err } - + items := make([]*service_dto.SimpleServiceItem, 0, len(services)) for _, p := range services { - + items = append(items, &service_dto.SimpleServiceItem{ Id: p.Id, Name: p.Name, @@ -151,14 +210,14 @@ func (i *imlServiceModule) Simple(ctx context.Context, keyword string) ([]*servi func (i *imlServiceModule) MySimple(ctx context.Context, keyword string) ([]*service_dto.SimpleServiceItem, error) { services, err := i.searchMyServices(ctx, "", keyword) - + if err != nil { return nil, err } - + items := make([]*service_dto.SimpleServiceItem, 0, len(services)) for _, p := range services { - + items = append(items, &service_dto.SimpleServiceItem{ Id: p.Id, Name: p.Name, @@ -178,7 +237,7 @@ func (i *imlServiceModule) Get(ctx context.Context, id string) (*service_dto.Ser if err != nil { return nil, err } - + s := service_dto.ToService(serviceInfo) s.Tags = auto.List(utils.SliceToSlice(tags, func(p *service_tag.Tag) string { return p.Tid @@ -201,24 +260,19 @@ func (i *imlServiceModule) Search(ctx context.Context, teamID string, keyword st if err != nil { return nil, err } - + serviceIds := utils.SliceToSlice(list, func(s *service.Service) string { return s.Id }) - - apiCountMap, err := i.apiService.CountByGroup(ctx, "", map[string]interface{}{"service": serviceIds}, "service") + + apiCountMap, err := i.apiDocService.APICountByServices(ctx, serviceIds...) if err != nil { return nil, err } - //serviceCountMap, err := i.serviceService.CountByGroup(ctx, "", map[string]interface{}{"uuid": serviceIds}, "service") - //if err != nil { - // return nil, err - //} - + items := make([]*service_dto.ServiceItem, 0, len(list)) for _, model := range list { apiCount := apiCountMap[model.Id] - //serviceCount := serviceCountMap[model.Id] items = append(items, &service_dto.ServiceItem{ Id: model.Id, Name: model.Name, @@ -234,7 +288,7 @@ func (i *imlServiceModule) Search(ctx context.Context, teamID string, keyword st } func (i *imlServiceModule) Create(ctx context.Context, teamID string, input *service_dto.CreateService) (*service_dto.Service, error) { - + if input.Id == "" { input.Id = uuid.New().String() } @@ -300,7 +354,7 @@ func (i *imlServiceModule) Edit(ctx context.Context, id string, input *service_d return fmt.Errorf("catalogue can not be empty") } } - + err = i.serviceService.Save(ctx, id, &service.Edit{ Name: input.Name, Description: input.Description, @@ -329,7 +383,7 @@ func (i *imlServiceModule) Edit(ctx context.Context, id string, input *service_d } return nil }) - + if err != nil { return nil, err } @@ -337,7 +391,7 @@ func (i *imlServiceModule) Edit(ctx context.Context, id string, input *service_d } func (i *imlServiceModule) Delete(ctx context.Context, id string) error { - + err := i.transaction.Transaction(ctx, func(ctx context.Context) error { count, err := i.apiService.CountByService(ctx, id) if err != nil { @@ -346,7 +400,7 @@ func (i *imlServiceModule) Delete(ctx context.Context, id string) error { if count > 0 { return fmt.Errorf("service has apis, can not delete") } - + return i.serviceService.Delete(ctx, id) }) return err @@ -389,7 +443,7 @@ func (i *imlServiceModule) getTagUuids(ctx context.Context, tags []string) ([]st func (i *imlServiceModule) ServiceDoc(ctx context.Context, pid string) (*serviceDto.ServiceDoc, error) { _, err := i.serviceService.Check(ctx, pid, map[string]bool{"as_server": true}) - + if err != nil { return nil, err } @@ -421,7 +475,7 @@ func (i *imlServiceModule) ServiceDoc(ctx context.Context, pid string) (*service func (i *imlServiceModule) SaveServiceDoc(ctx context.Context, pid string, input *serviceDto.SaveServiceDoc) error { _, err := i.serviceService.Check(ctx, pid, map[string]bool{"as_server": true}) - + if err != nil { return err } @@ -431,7 +485,10 @@ func (i *imlServiceModule) SaveServiceDoc(ctx context.Context, pid string, input }) } -var _ IAppModule = &imlAppModule{} +var ( + _ IAppModule = &imlAppModule{} + _ IExportAppModule = &imlAppModule{} +) type imlAppModule struct { teamService team.ITeamService `autowired:""` @@ -441,6 +498,21 @@ type imlAppModule struct { transaction store.ITransaction `autowired:""` } +func (i *imlAppModule) ExportAll(ctx context.Context) ([]*service_dto.ExportApp, error) { + apps, err := i.serviceService.AppList(ctx) + if err != nil { + return nil, err + } + return utils.SliceToSlice(apps, func(p *service.Service) *service_dto.ExportApp { + return &service_dto.ExportApp{ + Id: p.Id, + Name: p.Name, + Description: p.Description, + Team: p.Team, + } + }), nil +} + func (i *imlAppModule) Search(ctx context.Context, teamId string, keyword string) ([]*service_dto.AppItem, error) { var services []*service.Service var err error @@ -456,16 +528,16 @@ func (i *imlAppModule) Search(ctx context.Context, teamId string, keyword string if err != nil { return nil, err } - + serviceIds := utils.SliceToSlice(services, func(p *service.Service) string { return p.Id }) - + subscribers, err := i.subscribeService.SubscriptionsByApplication(ctx, serviceIds...) if err != nil { return nil, err } - + subscribeCount := map[string]int64{} subscribeVerifyCount := map[string]int64{} verifyTmp := map[string]struct{}{} @@ -484,7 +556,7 @@ func (i *imlAppModule) Search(ctx context.Context, teamId string, keyword string subscribeVerifyCount[s.Application]++ } default: - + } } items := make([]*service_dto.AppItem, 0, len(services)) @@ -516,7 +588,7 @@ func (i *imlAppModule) Search(ctx context.Context, teamId string, keyword string } func (i *imlAppModule) CreateApp(ctx context.Context, teamID string, input *service_dto.CreateApp) (*service_dto.App, error) { - + if input.Id == "" { input.Id = uuid.New().String() } @@ -536,11 +608,11 @@ func (i *imlAppModule) CreateApp(ctx context.Context, teamID string, input *serv if len(members) == 0 { return nil, fmt.Errorf("master is not in team") } - + err = i.transaction.Transaction(ctx, func(ctx context.Context) error { - + return i.serviceService.Create(ctx, mo) - + }) if err != nil { return nil, err @@ -560,7 +632,7 @@ func (i *imlAppModule) UpdateApp(ctx context.Context, appId string, input *servi //if info.Master != userId { // return nil, fmt.Errorf("user is not app master, can not update") //} - + err = i.serviceService.Save(ctx, appId, &service.Edit{ Name: input.Name, Description: input.Description, @@ -589,7 +661,7 @@ func (i *imlAppModule) searchMyApps(ctx context.Context, teamId string, keyword } teamIds := membersForUser[userID] condition["team"] = teamIds - + return i.serviceService.Search(ctx, keyword, condition, "update_at desc") } } @@ -602,12 +674,12 @@ func (i *imlAppModule) SearchMyApps(ctx context.Context, teamId string, keyword serviceIds := utils.SliceToSlice(services, func(p *service.Service) string { return p.Id }) - + subscribers, err := i.subscribeService.SubscriptionsByApplication(ctx, serviceIds...) if err != nil { return nil, err } - + subscribeCount := map[string]int64{} subscribeVerifyCount := map[string]int64{} verifyTmp := map[string]struct{}{} @@ -626,7 +698,7 @@ func (i *imlAppModule) SearchMyApps(ctx context.Context, teamId string, keyword subscribeVerifyCount[s.Application]++ } default: - + } } items := make([]*service_dto.AppItem, 0, len(services)) @@ -681,7 +753,7 @@ func (i *imlAppModule) MySimpleApps(ctx context.Context, keyword string) ([]*ser } items := make([]*service_dto.SimpleAppItem, 0, len(services)) for _, p := range services { - + items = append(items, &service_dto.SimpleAppItem{ Id: p.Id, Name: p.Name, @@ -722,6 +794,6 @@ func (i *imlAppModule) DeleteApp(ctx context.Context, appId string) error { if !info.AsApp { return errors.New("not app, can not delete") } - + return i.serviceService.Delete(ctx, appId) } diff --git a/module/service/module.go b/module/service/module.go index 64b7b9c1..0c69cf5a 100644 --- a/module/service/module.go +++ b/module/service/module.go @@ -2,10 +2,11 @@ package service import ( "context" + "github.com/APIParkLab/APIPark/module/system" "reflect" - + service_dto "github.com/APIParkLab/APIPark/module/service/dto" - + "github.com/eolinker/go-common/autowire" ) @@ -24,15 +25,19 @@ type IServiceModule interface { Delete(ctx context.Context, id string) error // Simple 获取简易项目列表 Simple(ctx context.Context, keyword string) ([]*service_dto.SimpleServiceItem, error) - + // MySimple 获取我的简易项目列表 MySimple(ctx context.Context, keyword string) ([]*service_dto.SimpleServiceItem, error) - + ServiceDoc(ctx context.Context, pid string) (*service_dto.ServiceDoc, error) // SaveServiceDoc 保存服务文档 SaveServiceDoc(ctx context.Context, pid string, input *service_dto.SaveServiceDoc) error } +type IExportServiceModule interface { + system.IExportModule[service_dto.ExportService] +} + type IAppModule interface { CreateApp(ctx context.Context, teamID string, input *service_dto.CreateApp) (*service_dto.App, error) UpdateApp(ctx context.Context, appId string, input *service_dto.UpdateApp) (*service_dto.App, error) @@ -45,13 +50,27 @@ type IAppModule interface { DeleteApp(ctx context.Context, appId string) error } -func init() { - autowire.Auto[IServiceModule](func() reflect.Value { - m := new(imlServiceModule) - return reflect.ValueOf(m) - }) - autowire.Auto[IAppModule](func() reflect.Value { - return reflect.ValueOf(new(imlAppModule)) - }) - +type IExportAppModule interface { + system.IExportModule[service_dto.ExportApp] +} + +func init() { + serviceModule := new(imlServiceModule) + autowire.Auto[IServiceModule](func() reflect.Value { + return reflect.ValueOf(serviceModule) + }) + + autowire.Auto[IExportServiceModule](func() reflect.Value { + return reflect.ValueOf(serviceModule) + }) + + appModule := new(imlAppModule) + autowire.Auto[IAppModule](func() reflect.Value { + return reflect.ValueOf(appModule) + }) + + autowire.Auto[IExportAppModule](func() reflect.Value { + return reflect.ValueOf(appModule) + }) + } diff --git a/module/subscribe/dto/output.go b/module/subscribe/dto/output.go index d3c8fe07..08b9e5d8 100644 --- a/module/subscribe/dto/output.go +++ b/module/subscribe/dto/output.go @@ -3,16 +3,13 @@ package subscribe_dto import "github.com/eolinker/go-common/auto" type Subscriber struct { - Id string `json:"id"` - Service auto.Label `json:"service" aolabel:"service"` - //Cluster []auto.Label `json:"partition" aolabel:"partition"` - + Id string `json:"id"` + Service auto.Label `json:"service" aolabel:"service"` Subscriber auto.Label `json:"subscriber" aolabel:"service"` Team auto.Label `json:"team" aolabel:"team"` ApplyTime auto.TimeLabel `json:"apply_time"` Applier auto.Label `json:"applier" aolabel:"user"` - //Approver auto.Label `json:"approver" aolabel:"user"` - From int `json:"from"` + From int `json:"from"` } type SubscriptionItem struct { @@ -54,9 +51,17 @@ type ApprovalItem struct { Status int `json:"status"` } -// -//type PartitionServiceItem struct { -// Id string `json:"id"` -// Name string `json:"name"` -// ServiceNum int64 `json:"service_num"` -//} +type ExportApproval struct { + Service string `json:"service"` + Application string `json:"application"` + Reason string `json:"reason"` +} + +type ExportSubscriber struct { + Id string `json:"id"` + Service string `json:"service"` + Subscriber string `json:"subscriber"` + Team string `json:"team"` + Applier string `json:"applier"` + From int `json:"from"` +} diff --git a/module/subscribe/iml.go b/module/subscribe/iml.go index 115df2e1..8d267f9a 100644 --- a/module/subscribe/iml.go +++ b/module/subscribe/iml.go @@ -26,7 +26,8 @@ import ( ) var ( - _ ISubscribeModule = (*imlSubscribeModule)(nil) + _ ISubscribeModule = (*imlSubscribeModule)(nil) + _ IExportSubscribeModule = (*imlSubscribeModule)(nil) ) type imlSubscribeModule struct { @@ -37,6 +38,31 @@ type imlSubscribeModule struct { transaction store.ITransaction `autowired:""` } +func (i *imlSubscribeModule) ExistSubscriber(ctx context.Context, serviceId string, app string) error { + _, err := i.subscribeService.GetByServiceAndApplication(ctx, serviceId, app) + if err == nil { + return nil + } + return err +} + +func (i *imlSubscribeModule) ExportAll(ctx context.Context) ([]*subscribe_dto.ExportSubscriber, error) { + list, err := i.subscribeService.List(ctx) + if err != nil { + return nil, err + } + + return utils.SliceToSlice(list, func(s *subscribe.Subscribe) *subscribe_dto.ExportSubscriber { + return &subscribe_dto.ExportSubscriber{ + Id: s.Id, + Service: s.Service, + Subscriber: s.Application, + Applier: s.Applier, + From: s.From, + } + }), nil +} + func (i *imlSubscribeModule) getSubscribers(ctx context.Context, serviceIds []string) ([]*gateway.SubscribeRelease, error) { subscribers, err := i.subscribeService.SubscribersByProject(ctx, serviceIds...) if err != nil { @@ -343,7 +369,10 @@ func (i *imlSubscribeModule) SearchSubscribers(ctx context.Context, serviceId st return items, nil } -var _ ISubscribeApprovalModule = (*imlSubscribeApprovalModule)(nil) +var ( + _ ISubscribeApprovalModule = (*imlSubscribeApprovalModule)(nil) + _ ISubscribeApprovalModule = (*imlSubscribeApprovalModule)(nil) +) type imlSubscribeApprovalModule struct { subscribeService subscribe.ISubscribeService `autowired:""` @@ -353,6 +382,21 @@ type imlSubscribeApprovalModule struct { transaction store.ITransaction `autowired:""` } +func (i *imlSubscribeApprovalModule) ExportAll(ctx context.Context) ([]*subscribe_dto.ExportApproval, error) { + + list, err := i.subscribeApplyService.List(ctx) + if err != nil { + return nil, err + } + return utils.SliceToSlice(list, func(s *subscribe.Apply) *subscribe_dto.ExportApproval { + return &subscribe_dto.ExportApproval{ + Service: s.Service, + Application: s.Application, + Reason: s.Reason, + } + }), nil +} + func (i *imlSubscribeApprovalModule) Pass(ctx context.Context, pid string, id string, approveInfo *subscribe_dto.Approve) error { applyInfo, err := i.subscribeApplyService.Get(ctx, id) if err != nil { diff --git a/module/subscribe/subscribe.go b/module/subscribe/subscribe.go index 5346295f..75480f43 100644 --- a/module/subscribe/subscribe.go +++ b/module/subscribe/subscribe.go @@ -2,16 +2,18 @@ package subscribe import ( "context" + "github.com/APIParkLab/APIPark/module/system" "reflect" - + "github.com/eolinker/go-common/autowire" - + subscribe_dto "github.com/APIParkLab/APIPark/module/subscribe/dto" ) type ISubscribeModule interface { // AddSubscriber 新增订阅方 AddSubscriber(ctx context.Context, pid string, input *subscribe_dto.AddSubscriber) error + ExistSubscriber(ctx context.Context, serviceId string, app string) error // DeleteSubscriber 删除订阅方 DeleteSubscriber(ctx context.Context, project string, serviceId string, applicationId string) error // SearchSubscribers 关键字获取订阅方列表 @@ -24,7 +26,12 @@ type ISubscribeModule interface { DeleteSubscription(ctx context.Context, pid string, uuid string) error // RevokeApply 取消申请 RevokeApply(ctx context.Context, app string, uuid string) error - //PartitionServices(ctx context.Context, app string) ([]*subscribe_dto.PartitionServiceItem, error) + + //ExportAll(ctx context.Context) ([]*subscribe_dto.ExportSubscriber, error) +} + +type IExportSubscribeModule interface { + system.IExportModule[subscribe_dto.ExportSubscriber] } type ISubscribeApprovalModule interface { @@ -36,14 +43,30 @@ type ISubscribeApprovalModule interface { Pass(ctx context.Context, pid string, id string, approveInfo *subscribe_dto.Approve) error // Reject 驳回审批 Reject(ctx context.Context, pid string, id string, approveInfo *subscribe_dto.Approve) error + + ExportAll(ctx context.Context) ([]*subscribe_dto.ExportApproval, error) +} + +type IExportSubscribeApprovalModule interface { + system.IExportModule[subscribe_dto.ExportApproval] } func init() { + subscribeModule := new(imlSubscribeModule) autowire.Auto[ISubscribeModule](func() reflect.Value { - return reflect.ValueOf(new(imlSubscribeModule)) + return reflect.ValueOf(subscribeModule) }) - + + autowire.Auto[IExportSubscribeModule](func() reflect.Value { + return reflect.ValueOf(subscribeModule) + }) + + applyModule := new(imlSubscribeApprovalModule) autowire.Auto[ISubscribeApprovalModule](func() reflect.Value { - return reflect.ValueOf(new(imlSubscribeApprovalModule)) + return reflect.ValueOf(applyModule) + }) + + autowire.Auto[IExportSubscribeApprovalModule](func() reflect.Value { + return reflect.ValueOf(applyModule) }) } diff --git a/module/system/system.go b/module/system/system.go new file mode 100644 index 00000000..411cec1e --- /dev/null +++ b/module/system/system.go @@ -0,0 +1,7 @@ +package system + +import "context" + +type IExportModule[T any] interface { + ExportAll(ctx context.Context) ([]*T, error) +} diff --git a/module/team/dto/output.go b/module/team/dto/output.go index 0b20ffe5..fb9eeb06 100644 --- a/module/team/dto/output.go +++ b/module/team/dto/output.go @@ -52,3 +52,9 @@ func ToTeam(model *team.Team, serviceNum int64, appNum int64) *Team { CanDelete: serviceNum == 0 && appNum == 0, } } + +type ExportTeam struct { + Id string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` +} diff --git a/module/team/iml.go b/module/team/iml.go index 2310c5f8..53df7fb5 100644 --- a/module/team/iml.go +++ b/module/team/iml.go @@ -3,26 +3,26 @@ package team import ( "context" "fmt" - "github.com/eolinker/go-common/utils" - + "github.com/eolinker/ap-account/service/role" - + "github.com/eolinker/go-common/store" - + "github.com/eolinker/ap-account/service/user" - + "github.com/APIParkLab/APIPark/service/service" team_member "github.com/APIParkLab/APIPark/service/team-member" - + "github.com/google/uuid" - + team_dto "github.com/APIParkLab/APIPark/module/team/dto" "github.com/APIParkLab/APIPark/service/team" ) var ( - _ ITeamModule = (*imlTeamModule)(nil) + _ ITeamModule = (*imlTeamModule)(nil) + _ ITeamExportModule = (*imlTeamModule)(nil) ) type imlTeamModule struct { @@ -35,6 +35,20 @@ type imlTeamModule struct { transaction store.ITransaction `autowired:""` } +func (m *imlTeamModule) ExportAll(ctx context.Context) ([]*team_dto.ExportTeam, error) { + teams, err := m.service.List(ctx) + if err != nil { + return nil, err + } + return utils.SliceToSlice(teams, func(t *team.Team) *team_dto.ExportTeam { + return &team_dto.ExportTeam{ + Id: t.Id, + Name: t.Name, + Description: t.Description, + } + }), nil +} + func (m *imlTeamModule) GetTeam(ctx context.Context, id string) (*team_dto.Team, error) { tv, err := m.service.Get(ctx, id) if err != nil { @@ -48,9 +62,9 @@ func (m *imlTeamModule) GetTeam(ctx context.Context, id string) (*team_dto.Team, if err != nil { return nil, err } - + return team_dto.ToTeam(tv, serviceCountMap[id], appCountMap[id]), nil - + } func (m *imlTeamModule) Search(ctx context.Context, keyword string) ([]*team_dto.Item, error) { @@ -58,7 +72,7 @@ func (m *imlTeamModule) Search(ctx context.Context, keyword string) ([]*team_dto if err != nil { return nil, err } - + serviceCountMap, err := m.serviceService.ServiceCountByTeam(ctx) if err != nil { return nil, err @@ -78,7 +92,7 @@ func (m *imlTeamModule) Create(ctx context.Context, input *team_dto.CreateTeam) if input.Id == "" { input.Id = uuid.New().String() } - + err := m.transaction.Transaction(ctx, func(ctx context.Context) error { if input.Master == "" { input.Master = utils.UserId(ctx) @@ -91,7 +105,7 @@ func (m *imlTeamModule) Create(ctx context.Context, input *team_dto.CreateTeam) if err != nil { return err } - + err = m.memberService.AddMemberTo(ctx, input.Id, input.Master) if err != nil { return err @@ -100,7 +114,7 @@ func (m *imlTeamModule) Create(ctx context.Context, input *team_dto.CreateTeam) if err != nil { return err } - + return m.roleMemberService.Add(ctx, &role.AddMember{ Role: supperRole.Id, User: input.Master, @@ -120,7 +134,7 @@ func (m *imlTeamModule) Edit(ctx context.Context, id string, input *team_dto.Edi Description: input.Description, }) }) - + if err != nil { return nil, err } diff --git a/module/team/team.go b/module/team/team.go index 584e5059..44c601cd 100644 --- a/module/team/team.go +++ b/module/team/team.go @@ -2,8 +2,9 @@ package team import ( "context" + "github.com/APIParkLab/APIPark/module/system" "reflect" - + team_dto "github.com/APIParkLab/APIPark/module/team/dto" "github.com/eolinker/go-common/autowire" ) @@ -13,17 +14,27 @@ type ITeamModule interface { GetTeam(ctx context.Context, id string) (*team_dto.Team, error) // Search 搜索团队 Search(ctx context.Context, keyword string) ([]*team_dto.Item, error) - + // Create 创建团队 Create(ctx context.Context, input *team_dto.CreateTeam) (*team_dto.Team, error) // Edit 编辑团队 Edit(ctx context.Context, id string, input *team_dto.EditTeam) (*team_dto.Team, error) // Delete 删除团队 Delete(ctx context.Context, id string) error + + //ExportAll(ctx context.Context) ([]*team_dto.ExportTeam, error) +} + +type ITeamExportModule interface { + system.IExportModule[team_dto.ExportTeam] } func init() { + teamModule := new(imlTeamModule) autowire.Auto[ITeamModule](func() reflect.Value { - return reflect.ValueOf(new(imlTeamModule)) + return reflect.ValueOf(teamModule) + }) + autowire.Auto[ITeamExportModule](func() reflect.Value { + return reflect.ValueOf(teamModule) }) } diff --git a/module/upstream/dto/output.go b/module/upstream/dto/output.go index ade127c4..298b7a69 100644 --- a/module/upstream/dto/output.go +++ b/module/upstream/dto/output.go @@ -2,6 +2,14 @@ package upstream_dto type UpstreamConfig *Upstream +type ExportUpstream struct { + ID string `json:"id"` + Name string `json:"name"` + Service string `json:"service"` + + *Upstream +} + type Upstream struct { Type string `json:"driver"` Balance string `json:"balance"` diff --git a/module/upstream/iml.go b/module/upstream/iml.go index 516a6588..67a9a03b 100644 --- a/module/upstream/iml.go +++ b/module/upstream/iml.go @@ -4,34 +4,66 @@ import ( "context" "errors" "fmt" - + "github.com/APIParkLab/APIPark/service/universally/commit" + "github.com/eolinker/go-common/utils" + "github.com/APIParkLab/APIPark/service/cluster" "github.com/APIParkLab/APIPark/service/service" - + "gorm.io/gorm" - + "github.com/APIParkLab/APIPark/service/upstream" - + "github.com/eolinker/go-common/store" - + upstream_dto "github.com/APIParkLab/APIPark/module/upstream/dto" ) var ( - _ IUpstreamModule = (*imlUpstreamModule)(nil) - asServer = map[string]bool{ + _ IUpstreamModule = (*imlUpstreamModule)(nil) + _ IExportUpstreamModule = (*imlUpstreamModule)(nil) + asServer = map[string]bool{ "as_server": true, } ) type imlUpstreamModule struct { - projectService service.IServiceService `autowired:""` + serviceService service.IServiceService `autowired:""` upstreamService upstream.IUpstreamService `autowired:""` transaction store.ITransaction `autowired:""` } +func (i *imlUpstreamModule) ExportAll(ctx context.Context) ([]*upstream_dto.ExportUpstream, error) { + latestCommits, err := i.upstreamService.ListLatestCommit(ctx) + if err != nil { + return nil, err + } + commitMap := utils.SliceToMap(latestCommits, func(c *commit.Commit[upstream.Config]) string { + return c.Target + }) + list, err := i.upstreamService.List(ctx) + if err != nil { + return nil, err + } + items := make([]*upstream_dto.ExportUpstream, 0, len(list)) + for _, u := range list { + c, ok := commitMap[u.UUID] + if !ok { + continue + } + + items = append(items, &upstream_dto.ExportUpstream{ + ID: u.UUID, + Name: u.Name, + Service: u.Service, + Upstream: upstream_dto.FromClusterConfig(c.Data), + }) + } + return items, nil +} + func (i *imlUpstreamModule) Get(ctx context.Context, pid string) (upstream_dto.UpstreamConfig, error) { - _, err := i.projectService.Check(ctx, pid, asServer) + _, err := i.serviceService.Check(ctx, pid, asServer) if err != nil { return nil, err } @@ -49,29 +81,29 @@ func (i *imlUpstreamModule) Get(ctx context.Context, pid string) (upstream_dto.U } return nil, nil } - + return upstream_dto.FromClusterConfig(commit.Data), nil } func (i *imlUpstreamModule) Save(ctx context.Context, pid string, upstreamConfig upstream_dto.UpstreamConfig) (upstream_dto.UpstreamConfig, error) { - pInfo, err := i.projectService.Check(ctx, pid, asServer) + pInfo, err := i.serviceService.Check(ctx, pid, asServer) if err != nil { return nil, err } - + err = i.transaction.Transaction(ctx, func(ctx context.Context) error { err = i.upstreamService.SaveCommit(ctx, pid, cluster.DefaultClusterID, upstream_dto.ConvertUpstream(upstreamConfig)) if err != nil { return err } - + return i.upstreamService.Save(ctx, &upstream.SaveUpstream{ UUID: pid, Name: fmt.Sprintf("upstream-%s", pid), - Project: pid, + Service: pid, Team: pInfo.Team, }) - + }) if err != nil { return nil, err diff --git a/module/upstream/upstream.go b/module/upstream/upstream.go index c5c93c21..13b871a9 100644 --- a/module/upstream/upstream.go +++ b/module/upstream/upstream.go @@ -2,20 +2,31 @@ package upstream import ( "context" + "github.com/APIParkLab/APIPark/module/system" "reflect" - + "github.com/eolinker/go-common/autowire" - + upstream_dto "github.com/APIParkLab/APIPark/module/upstream/dto" ) type IUpstreamModule interface { Get(ctx context.Context, pid string) (upstream_dto.UpstreamConfig, error) Save(ctx context.Context, pid string, upstream upstream_dto.UpstreamConfig) (upstream_dto.UpstreamConfig, error) + //ExportAll(ctx context.Context) ([]*upstream_dto.ExportUpstream, error) +} + +type IExportUpstreamModule interface { + system.IExportModule[upstream_dto.ExportUpstream] } func init() { + upstreamModule := new(imlUpstreamModule) autowire.Auto[IUpstreamModule](func() reflect.Value { - return reflect.ValueOf(new(imlUpstreamModule)) + return reflect.ValueOf(upstreamModule) + }) + + autowire.Auto[IExportUpstreamModule](func() reflect.Value { + return reflect.ValueOf(upstreamModule) }) } diff --git a/plugins/core/api.go b/plugins/core/api.go index 7bd016ec..80a2c1ab 100644 --- a/plugins/core/api.go +++ b/plugins/core/api.go @@ -8,17 +8,16 @@ import ( func (p *plugin) apiApis() []pm3.Api { return []pm3.Api{ - pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/service/apis", []string{"context", "query:keyword", "query:service"}, []string{"apis"}, p.apiController.Search), - pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/service/apis/simple", []string{"context", "query:keyword", "query:service"}, []string{"apis"}, p.apiController.SimpleSearch), - //pm3.CreateApiWidthDoc(http.MethodPost, "/api/v1/simple/service/apis", []string{"context", "query:service"}, []string{"apis"}, p.apiController.SimpleList), - pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/service/api/detail", []string{"context", "query:service", "query:api"}, []string{"api"}, p.apiController.Detail), - pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/service/api/detail/simple", []string{"context", "query:service", "query:api"}, []string{"api"}, p.apiController.SimpleDetail), - pm3.CreateApiWidthDoc(http.MethodPost, "/api/v1/service/api", []string{"context", "query:service", "body"}, []string{"api"}, p.apiController.Create), - pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/service/api", []string{"context", "query:service", "query:api", "body"}, []string{"api"}, p.apiController.Edit), - pm3.CreateApiWidthDoc(http.MethodDelete, "/api/v1/service/api", []string{"context", "query:service", "query:api"}, nil, p.apiController.Delete), - pm3.CreateApiWidthDoc(http.MethodPost, "/api/v1/service/api/copy", []string{"context", "query:service", "query:api", "body"}, []string{"api"}, p.apiController.Copy), - pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/service/api/doc", []string{"context", "query:service", "query:api"}, []string{"api"}, p.apiController.ApiDocDetail), - pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/service/api/proxy", []string{"context", "query:service", "query:api"}, []string{"api"}, p.apiController.ApiProxyDetail), - pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/service/api/define", []string{"context", "query:service"}, []string{"prefix", "force"}, p.apiController.Prefix), + pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/service/routers", []string{"context", "query:keyword", "query:service"}, []string{"routers"}, p.routerController.Search), + pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/service/router/detail", []string{"context", "query:service", "query:router"}, []string{"router"}, p.routerController.Detail), + pm3.CreateApiWidthDoc(http.MethodPost, "/api/v1/service/router", []string{"context", "query:service", "body"}, []string{"router"}, p.routerController.Create), + pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/service/router", []string{"context", "query:service", "query:router", "body"}, []string{"router"}, p.routerController.Edit), + pm3.CreateApiWidthDoc(http.MethodDelete, "/api/v1/service/router", []string{"context", "query:service", "query:router"}, nil, p.routerController.Delete), + pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/service/router/define", []string{"context", "query:service"}, []string{"prefix", "force"}, p.routerController.Prefix), + + pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/service/api_doc", []string{"context", "query:service", "body"}, []string{"doc"}, p.apiDocController.UpdateDoc), + pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/service/api_doc", []string{"context", "query:service"}, []string{"doc"}, p.apiDocController.GetDoc), + + pm3.CreateApiWidthDoc(http.MethodPost, "/api/v1/service/api_doc/upload", []string{"context", "query:service"}, []string{"doc"}, p.apiDocController.UploadDoc), } } diff --git a/plugins/core/core.go b/plugins/core/core.go index 7167e0be..9b870fb3 100644 --- a/plugins/core/core.go +++ b/plugins/core/core.go @@ -2,6 +2,8 @@ package core import ( "github.com/APIParkLab/APIPark/controller/monitor" + "github.com/APIParkLab/APIPark/controller/router" + "github.com/APIParkLab/APIPark/controller/system" "net/http" plugin_cluster "github.com/APIParkLab/APIPark/controller/plugin-cluster" @@ -20,8 +22,6 @@ import ( "github.com/APIParkLab/APIPark/controller/subscribe" - "github.com/APIParkLab/APIPark/controller/api" - "github.com/APIParkLab/APIPark/controller/upstream" "github.com/APIParkLab/APIPark/controller/service" @@ -65,7 +65,8 @@ type plugin struct { monitorConfigController monitor.IMonitorConfigController `autowired:""` catalogueController catalogue.ICatalogueController `autowired:""` upstreamController upstream.IUpstreamController `autowired:""` - apiController api.IAPIController `autowired:""` + routerController router.IRouterController `autowired:""` + apiDocController router.IAPIDocController `autowired:""` subscribeController subscribe.ISubscribeController `autowired:""` appAuthorizationController application_authorization.IAuthorizationController `autowired:""` releaseController release.IReleaseController `autowired:""` @@ -74,6 +75,8 @@ type plugin struct { dynamicModuleController dynamic_module.IDynamicModuleController `autowired:""` pluginClusterController plugin_cluster.IPluginClusterController `autowired:""` commonController common.ICommonController `autowired:""` + exportConfigController system.IExportConfigController `autowired:""` + importConfigController system.IImportConfigController `autowired:""` apis []pm3.Api } @@ -93,6 +96,7 @@ func (p *plugin) OnComplete() { p.apis = append(p.apis, p.monitorStatisticApis()...) p.apis = append(p.apis, p.PartitionPluginApi()...) p.apis = append(p.apis, p.commonApis()...) + p.apis = append(p.apis, p.systemApis()...) } func (p *plugin) Name() string { diff --git a/plugins/core/dynamic-module.go b/plugins/core/dynamic-module.go index 0c4e4305..94dd7e26 100644 --- a/plugins/core/dynamic-module.go +++ b/plugins/core/dynamic-module.go @@ -2,7 +2,7 @@ package core import ( "net/http" - + "github.com/eolinker/go-common/pm3" ) diff --git a/plugins/core/monitor.go b/plugins/core/monitor.go index 9807da3e..ca6025b1 100644 --- a/plugins/core/monitor.go +++ b/plugins/core/monitor.go @@ -15,5 +15,6 @@ func (p *plugin) monitorStatisticApis() []pm3.Api { pm3.CreateApiWidthDoc(http.MethodPost, "/api/v1/monitor/config", []string{"context", "body"}, []string{"info"}, p.monitorConfigController.SaveMonitorConfig), pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/monitor/config", []string{"context"}, []string{"info"}, p.monitorConfigController.GetMonitorConfig), + pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/simple/monitor/clusters", []string{"context"}, []string{"clusters"}, p.monitorConfigController.GetMonitorCluster), } } diff --git a/plugins/core/system.go b/plugins/core/system.go new file mode 100644 index 00000000..a90fc16f --- /dev/null +++ b/plugins/core/system.go @@ -0,0 +1,13 @@ +package core + +import ( + "github.com/eolinker/go-common/pm3" + "net/http" +) + +func (p *plugin) systemApis() []pm3.Api { + return []pm3.Api{ + pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/system/_export", []string{"context"}, nil, p.exportConfigController.ExportAll), + pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/system/_import", []string{"context"}, nil, p.importConfigController.ImportAll), + } +} diff --git a/resources/access/access.yaml b/resources/access/access.yaml index 7085e997..90ac8375 100644 --- a/resources/access/access.yaml +++ b/resources/access/access.yaml @@ -94,7 +94,7 @@ system: - "PUT:/api/v1/catalogue/sort" dependents: - system.api_market.service_classification.view - - name: devops + - name: Devops cname: 运维 value: 'devops' children: @@ -132,7 +132,22 @@ system: - "POST:/api/v1/certificate" - "PUT:/api/v1/certificate" - "DELETE:/api/v1/certificate" - + - name: Data Source + cname: '数据源' + value: 'data_source' + children: + - name: view + cname: '查看' + value: 'view' + guest_allow: true + apis: + - "GET:/api/v1/monitor/config" + - name: manager + cname: '管理' + value: 'manager' + apis: + - "POST:/api/v1/monitor/config" + - "PUT:/api/v1/monitor/config" - name: log configuration cname: 日志 value: 'log_configuration' @@ -154,6 +169,38 @@ system: - "DELETE:/api/v1/dynamic/{name}/batch" - "PUT:/api/v1/dynamic/{name}/online" - "PUT:/api/v1/dynamic/{name}/offline" + - name: monitor + cname: 监控 + value: 'monitor' + children: + - name: view + cname: 查看 + value: 'view' + guest_allow: true + apis: + - "GET:/api/v1/monitor/config" + - name: manager + cname: 管理 + value: 'manager' + apis: + - "POST:/api/v1/monitor/config" + - name: dashboard + cname: 仪表盘 + value: 'dashboard' + children: + - name: run view + cname: 运行视图 + value: 'run_view' + children: + - name: view + cname: 查看 + value: 'view' + guest_allow: true + apis: + - "GET:/api/v1/monitor/overview/invoke" + - "GET:/api/v1/monitor/overview/message" + - "GET:/api/v1/monitor/overview/top10" + - "GET:/api/v1/monitor/overview/summary" - name: workspace cname: 工作空间 value: 'workspace' @@ -204,28 +251,42 @@ team: cname: 服务 value: 'service' children: - - name: api - cname: API - value: 'api' + - name: router + cname: 路由 + value: 'router' children: - name: view cname: 查看 value: 'view' guest_allow: true apis: - - "GET:/api/v1/service/apis" - - "GET:/api/v1/service/api/detail" - - "GET:/api/v1/service/api/detail/simple" - - "GET:/api/v1/service/api/define" - - "GET:/api/v1/service/apis/simple" + - "GET:/api/v1/service/routers" + - "GET:/api/v1/service/router/detail" + - "GET:/api/v1/service/router/detail/simple" + - "GET:/api/v1/service/router/define" - name: manager cname: 管理 value: 'manager' apis: - - "POST:/api/v1/service/api" - - "PUT:/api/v1/service/api" - - "DELETE:/api/v1/service/api" - - "POST:/api/v1/service/api/copy" + - "POST:/api/v1/service/router" + - "PUT:/api/v1/service/router" + - "DELETE:/api/v1/service/router" + - name: api_doc + cname: API文档 + value: 'api_doc' + children: + - name: view + cname: 查看 + value: 'view' + guest_allow: true + apis: + - "GET:/api/v1/service/api_doc" + - name: manager + cname: 管理 + value: 'manager' + apis: + - "PUT:/api/v1/service/api_doc" + - "POST:/api/v1/service/api_doc/upload" - name: upstream cname: 上游 value: 'upstream' diff --git a/resources/access/role.yaml b/resources/access/role.yaml index f4e7233d..8f5991fb 100644 --- a/resources/access/role.yaml +++ b/resources/access/role.yaml @@ -4,12 +4,15 @@ system: permits: - system.api_market.service_classification.manager - system.api_market.service_classification.view + - system.dashboard.run_view.view - system.devops.cluster.manager - system.devops.cluster.view - system.devops.log_configuration.manager - system.devops.log_configuration.view - system.devops.ssl_certificate.manager - system.devops.ssl_certificate.view + - system.devops.data_source.manager + - system.devops.data_source.view - system.organization.member.manager - system.organization.member.view - system.organization.role.view_system_role @@ -36,12 +39,15 @@ system: permits: - system.api_market.service_classification.manager - system.api_market.service_classification.view + - system.dashboard.run_view.view - system.devops.cluster.manager - system.devops.cluster.view - system.devops.log_configuration.manager - system.devops.log_configuration.view - system.devops.ssl_certificate.manager - system.devops.ssl_certificate.view + - system.devops.data_source.manager + - system.devops.data_source.view - system.workspace.api_market.view - system.workspace.application.view_all - system.workspace.service.view_all @@ -58,6 +64,7 @@ system: - system.devops.cluster.view - system.devops.log_configuration.view - system.devops.ssl_certificate.view + - system.devops.monitor.view - system.organization.member.view - system.organization.role.view_system_role - system.organization.role.view_team_role @@ -66,6 +73,7 @@ system: - system.workspace.application.view_all - system.workspace.service.view_all - system.workspace.team.view_all + - system.dashboard.run_view.view team: - name: team_admin cname: 团队管理员 @@ -75,8 +83,10 @@ team: - team.application.authorization.view - team.application.subscription.manager - team.application.subscription.view - - team.service.api.manager - - team.service.api.view + - team.service.api_doc.view + - team.service.api_doc.manager + - team.service.router.manager + - team.service.router.view - team.service.release.manager - team.service.release.view - team.service.service.manager @@ -95,8 +105,10 @@ team: - team.service.service.manager - team.service.upstream.manager - team.service.upstream.view - - team.service.api.manager - - team.service.api.view + - team.service.api_doc.view + - team.service.api_doc.manager + - team.service.router.manager + - team.service.router.view - team.service.subscription.manager - team.service.subscription.view - team.service.release.manager @@ -107,8 +119,10 @@ team: permits: - team.service.upstream.manager - team.service.upstream.view - - team.service.api.manager - - team.service.api.view + - team.service.api_doc.view + - team.service.api_doc.manager + - team.service.router.manager + - team.service.router.view - team.service.release.manager - team.service.release.view - team.team.member.view @@ -134,7 +148,7 @@ team: permits: - team.application.authorization.view - team.application.subscription.view - - team.service.api.view + - team.service.router.view - team.service.release.view - team.service.subscription.view - team.service.upstream.view diff --git a/service/api-doc/iml.go b/service/api-doc/iml.go new file mode 100644 index 00000000..393f6754 --- /dev/null +++ b/service/api-doc/iml.go @@ -0,0 +1,140 @@ +package api_doc + +import ( + "context" + "errors" + "github.com/APIParkLab/APIPark/service/universally/commit" + "github.com/APIParkLab/APIPark/stores/api" + "github.com/eolinker/go-common/utils" + "gorm.io/gorm" + "time" +) + +var ( + _ IAPIDocService = (*imlAPIDocService)(nil) +) + +type imlAPIDocService struct { + store api.IAPIDocStore `autowired:""` + commitService commit.ICommitWithKeyService[DocCommit] `autowired:""` +} + +func (i *imlAPIDocService) LatestAPICountByCommits(ctx context.Context, commitIds ...string) (map[string]int64, error) { + commits, err := i.commitService.List(ctx, commitIds...) + if err != nil { + return nil, err + } + countMap := make(map[string]int64) + for _, c := range commits { + doc, err := NewDocLoader(c.Data.Content) + if err != nil { + return nil, err + } + countMap[c.Target] = doc.APICount() + } + return countMap, nil +} + +func (i *imlAPIDocService) GetDocCommit(ctx context.Context, commitId string) (*commit.Commit[DocCommit], error) { + return i.commitService.Get(ctx, commitId) +} + +func (i *imlAPIDocService) ListDocCommit(ctx context.Context, commitIds ...string) ([]*commit.Commit[DocCommit], error) { + return i.commitService.List(ctx, commitIds...) +} + +func (i *imlAPIDocService) ListLatestDocCommit(ctx context.Context, serviceIds ...string) ([]*commit.Commit[DocCommit], error) { + return i.commitService.ListLatest(ctx, serviceIds...) +} + +func (i *imlAPIDocService) LatestAPICountByServices(ctx context.Context, serviceIds ...string) (map[string]int64, error) { + list, err := i.commitService.ListLatest(ctx, serviceIds...) + if err != nil { + return nil, err + } + return utils.SliceToMapO(list, func(i *commit.Commit[DocCommit]) (string, int64) { + return i.Target, i.Data.APICount + }), nil +} + +func (i *imlAPIDocService) APICountByServices(ctx context.Context, serviceIds ...string) (map[string]int64, error) { + w := make(map[string]interface{}) + if len(serviceIds) > 0 { + w["service"] = serviceIds + } + list, err := i.store.List(ctx, w) + if err != nil { + return nil, err + } + return utils.SliceToMapO(list, func(i *api.Doc) (string, int64) { + return i.Service, i.APICount + }), nil +} + +func (i *imlAPIDocService) UpdateDoc(ctx context.Context, serviceId string, input *UpdateDoc) error { + doc, err := NewDocLoader(input.Content) + if err != nil { + return err + } + if err := doc.Valid(); err != nil { + return err + } + + info, err := i.store.First(ctx, map[string]interface{}{ + "service": serviceId, + }) + operator := utils.UserId(ctx) + if err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + return err + } + return i.store.Insert(ctx, &api.Doc{ + UUID: input.ID, + Service: serviceId, + Content: input.Content, + Updater: operator, + UpdateAt: time.Now(), + APICount: doc.APICount(), + }) + } + info.Updater = operator + info.UpdateAt = time.Now() + info.Content = input.Content + info.APICount = doc.APICount() + return i.store.Save(ctx, info) +} + +func (i *imlAPIDocService) GetDoc(ctx context.Context, serviceId string) (*Doc, error) { + info, err := i.store.First(ctx, map[string]interface{}{ + "service": serviceId, + }) + if err != nil { + return nil, err + } + return &Doc{ + Id: info.UUID, + Service: info.Service, + Content: info.Content, + Updater: info.Updater, + UpdateAt: info.UpdateAt, + }, nil +} + +func (i *imlAPIDocService) LatestDocCommit(ctx context.Context, serviceId string) (*commit.Commit[DocCommit], error) { + return i.commitService.Latest(ctx, serviceId) +} + +func (i *imlAPIDocService) CommitDoc(ctx context.Context, serviceId string, data *Doc) error { + doc, err := NewDocLoader(data.Content) + if err != nil { + return err + } + if err := doc.Valid(); err != nil { + return err + } + + return i.commitService.Save(ctx, serviceId, &DocCommit{ + Content: data.Content, + APICount: doc.APICount(), + }) +} diff --git a/service/api-doc/model.go b/service/api-doc/model.go new file mode 100644 index 00000000..af57aba1 --- /dev/null +++ b/service/api-doc/model.go @@ -0,0 +1,22 @@ +package api_doc + +import "time" + +type UpdateDoc struct { + ID string + Service string + Content string +} + +type Doc struct { + Id string + Service string + Content string + Updater string + UpdateAt time.Time +} + +type DocCommit struct { + Content string `json:"content"` + APICount int64 `json:"api_count"` +} diff --git a/service/api-doc/service.go b/service/api-doc/service.go new file mode 100644 index 00000000..cfd098e2 --- /dev/null +++ b/service/api-doc/service.go @@ -0,0 +1,101 @@ +package api_doc + +import ( + "context" + "fmt" + "github.com/APIParkLab/APIPark/service/universally/commit" + "github.com/eolinker/go-common/autowire" + "github.com/getkin/kin-openapi/openapi3" + "reflect" +) + +type IAPIDocService interface { + // UpdateDoc 更新文档 + UpdateDoc(ctx context.Context, serviceId string, input *UpdateDoc) error + // GetDoc 获取文档 + GetDoc(ctx context.Context, serviceId string) (*Doc, error) + + CommitDoc(ctx context.Context, serviceId string, data *Doc) error + + APICountByServices(ctx context.Context, serviceIds ...string) (map[string]int64, error) + + GetDocCommit(ctx context.Context, commitId string) (*commit.Commit[DocCommit], error) + // LatestDocCommit 获取最新文档 + LatestDocCommit(ctx context.Context, serviceId string) (*commit.Commit[DocCommit], error) + ListLatestDocCommit(ctx context.Context, serviceIds ...string) ([]*commit.Commit[DocCommit], error) + ListDocCommit(ctx context.Context, commitIds ...string) ([]*commit.Commit[DocCommit], error) + LatestAPICountByServices(ctx context.Context, serviceIds ...string) (map[string]int64, error) + LatestAPICountByCommits(ctx context.Context, commitIds ...string) (map[string]int64, error) +} + +func init() { + autowire.Auto[IAPIDocService](func() reflect.Value { + return reflect.ValueOf(new(imlAPIDocService)) + }) + commit.InitCommitWithKeyService[DocCommit]("service", "api_doc") +} + +const ( + openAPIv3 = "v3" + openAPIv2 = "v2" +) + +var ( + openapi3Loader = openapi3.NewLoader() +) + +type DocLoader struct { + openAPI3Doc *openapi3.T + version string +} + +func NewDocLoader(content string) (*DocLoader, error) { + doc, err := openapi3Loader.LoadFromData([]byte(content)) + if err != nil { + return nil, fmt.Errorf("load openAPI3Doc error:%v", err) + } + + return &DocLoader{openAPI3Doc: doc}, nil +} + +func (d *DocLoader) Valid() error { + if d.openAPI3Doc == nil { + return fmt.Errorf("openAPI3Doc is nil") + } + + return d.openAPI3Doc.Validate(openapi3Loader.Context) +} + +func (d *DocLoader) APICount() int64 { + if d.openAPI3Doc == nil || d.openAPI3Doc.Paths == nil { + return 0 + } + var count int64 + for _, item := range d.openAPI3Doc.Paths.Map() { + if item.Get != nil { + count++ + } + if item.Post != nil { + count++ + } + if item.Put != nil { + count++ + } + if item.Patch != nil { + count++ + } + if item.Delete != nil { + count++ + } + if item.Head != nil { + count++ + } + if item.Options != nil { + count++ + } + if item.Trace != nil { + count++ + } + } + return count +} diff --git a/service/api/iml.go b/service/api/iml.go index 5f1b45f4..12f42c25 100644 --- a/service/api/iml.go +++ b/service/api/iml.go @@ -5,18 +5,18 @@ import ( "errors" "fmt" "time" - + "github.com/google/uuid" "gorm.io/gorm" - + "github.com/APIParkLab/APIPark/service/universally/commit" - + "github.com/APIParkLab/APIPark/stores/api" - + "github.com/eolinker/go-common/utils" - + "github.com/eolinker/go-common/auto" - + "github.com/APIParkLab/APIPark/service/universally" ) @@ -27,19 +27,39 @@ var ( type HistoryType string const ( - HistoryDocument HistoryType = "doc" - HistoryProxy HistoryType = "proxy" + HistoryRequest HistoryType = "request" + HistoryProxy HistoryType = "proxy" ) type imlAPIService struct { - store api.IApiBaseStore `autowired:""` - apiInfoStore api.IAPIInfoStore `autowired:""` - proxyCommitService commit.ICommitWithKeyService[Proxy] `autowired:""` - documentCommitService commit.ICommitWithKeyService[Document] `autowired:""` + store api.IApiBaseStore `autowired:""` + apiInfoStore api.IAPIInfoStore `autowired:""` + requestCommitService commit.ICommitWithKeyService[Request] `autowired:""` + proxyCommitService commit.ICommitWithKeyService[Proxy] `autowired:""` universally.IServiceGet[API] universally.IServiceDelete } +func (i *imlAPIService) ListLatestCommitRequest(ctx context.Context, aid ...string) ([]*commit.Commit[Request], error) { + return i.requestCommitService.ListLatest(ctx, aid...) +} + +func (i *imlAPIService) LatestRequest(ctx context.Context, aid string) (*commit.Commit[Request], error) { + return i.requestCommitService.Latest(ctx, aid) +} + +func (i *imlAPIService) GetRequestCommit(ctx context.Context, commitId string) (*commit.Commit[Request], error) { + return i.requestCommitService.Get(ctx, commitId) +} + +func (i *imlAPIService) ListRequestCommit(ctx context.Context, commitId ...string) ([]*commit.Commit[Request], error) { + return i.requestCommitService.List(ctx, commitId...) +} + +func (i *imlAPIService) SaveRequest(ctx context.Context, aid string, data *Request) error { + return i.requestCommitService.Save(ctx, aid, data) +} + func (i *imlAPIService) CountMapByService(ctx context.Context, service ...string) (map[string]int64, error) { w := map[string]interface{}{} if len(service) > 0 { @@ -52,7 +72,7 @@ func (i *imlAPIService) ListInfoForService(ctx context.Context, serviceId string apis, err := i.store.List(ctx, map[string]interface{}{ "service": serviceId, }) - aids := utils.SliceToSlice(apis, func(a *api.Api) int64 { + aids := utils.SliceToSlice(apis, func(a *api.API) int64 { return a.Id }) list, err := i.apiInfoStore.List(ctx, map[string]interface{}{ @@ -62,72 +82,31 @@ func (i *imlAPIService) ListInfoForService(ctx context.Context, serviceId string if err != nil { return nil, err } - return utils.SliceToSlice(list, func(info *api.Info) *Info { - return &Info{ - UUID: info.UUID, - Name: info.Name, - Description: info.Description, - CreateAt: info.CreateAt, - UpdateAt: info.UpdateAt, - Service: info.Service, - Team: info.Team, - Creator: info.Creator, - Updater: info.Updater, - Method: info.Method, - Path: info.Path, - Match: info.Match, - } - }), nil + return utils.SliceToSlice(list, FromEntityInfo), nil } func (i *imlAPIService) ListInfo(ctx context.Context, aids ...string) ([]*Info, error) { - list, err := i.apiInfoStore.List(ctx, map[string]interface{}{ - "uuid": aids, - }) + w := map[string]interface{}{} + if len(aids) > 0 { + w["uuid"] = aids + } + list, err := i.apiInfoStore.List(ctx, w) if err != nil { return nil, err } - return utils.SliceToSlice(list, func(info *api.Info) *Info { - return &Info{ - UUID: info.UUID, - Name: info.Name, - Description: info.Description, - CreateAt: info.CreateAt, - UpdateAt: info.UpdateAt, - Service: info.Service, - Team: info.Team, - Creator: info.Creator, - Updater: info.Updater, - Method: info.Method, - Path: info.Path, - Match: info.Match, - } - }), nil + return utils.SliceToSlice(list, FromEntityInfo), nil } func (i *imlAPIService) GetInfo(ctx context.Context, aid string) (*Info, error) { - + info, err := i.apiInfoStore.GetByUUID(ctx, aid) if err != nil { return nil, err } - return &Info{ - UUID: info.UUID, - Name: info.Name, - Description: info.Description, - CreateAt: info.CreateAt, - UpdateAt: info.UpdateAt, - Service: info.Service, - Team: info.Team, - Creator: info.Creator, - Updater: info.Updater, - Method: info.Method, - Path: info.Path, - Match: info.Match, - }, nil + return FromEntityInfo(info), nil } -func (i *imlAPIService) Save(ctx context.Context, id string, model *EditAPI) error { +func (i *imlAPIService) Save(ctx context.Context, id string, model *Edit) error { if model == nil { return errors.New("input is nil") } @@ -136,67 +115,61 @@ func (i *imlAPIService) Save(ctx context.Context, id string, model *EditAPI) err if err != nil { return err } - if model.Name != nil { - ev.Name = *model.Name - } - //if model.Upstream != nil { - // ev.Upstream = *model.Upstream - //} + if model.Description != nil { ev.Description = *model.Description } + if model.Protocols != nil { + ev.Protocol = *model.Protocols + } + if model.Path != nil { + ev.Path = *model.Path + } + if model.Match != nil { + ev.Match = *model.Match + } + if model.Methods != nil { + ev.Method = *model.Methods + } + if model.Disable != nil { + ev.Disable = *model.Disable + } + e := i.apiInfoStore.Save(ctx, ev) if e != nil { return e } return i.store.SetLabels(ctx, ev.Id, getLabels(ev)...) - + }) } func getLabels(input *api.Info, appends ...string) []string { labels := make([]string, 0, len(appends)+9) - labels = append(labels, input.UUID, input.Name, input.Description, input.Method, input.Path, input.Service, input.Team, input.Updater) + labels = append(labels, input.UUID, input.Name, input.Description, input.Path, input.Service, input.Team, input.Updater) labels = append(labels, appends...) return labels } -func (i *imlAPIService) Create(ctx context.Context, input *CreateAPI) (err error) { +func (i *imlAPIService) Create(ctx context.Context, input *Create) (err error) { operater := utils.UserId(ctx) return i.store.Transaction(ctx, func(ctx context.Context) error { - t, err := i.store.First(ctx, map[string]interface{}{ - "method": input.Method, - "path": input.Path, - }) - if err != nil { - if !errors.Is(err, gorm.ErrRecordNotFound) { - return err - } - } - - if t != nil { - return fmt.Errorf("method(%s),path(%s) is exist", input.Method, input.Path) - } if input.UUID != "" { - a, err := i.store.GetByUUID(ctx, input.UUID) + _, err = i.store.GetByUUID(ctx, input.UUID) if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { return err } - if a != nil { - return fmt.Errorf("api(%s) is exist", input.UUID) - } - + } else { input.UUID = uuid.NewString() } - - ne := api.Api{ - Id: 0, + + ne := api.API{ UUID: input.UUID, + Name: input.UUID, Service: input.Service, Team: input.Team, Creator: operater, CreateAt: time.Now(), - IsDelete: 0, - Method: input.Method, + Method: input.Methods, Path: input.Path, } err = i.store.Insert(ctx, &ne) @@ -206,18 +179,17 @@ func (i *imlAPIService) Create(ctx context.Context, input *CreateAPI) (err error ev := &api.Info{ Id: ne.Id, UUID: ne.UUID, - Name: input.Name, + Name: ne.UUID, Description: input.Description, Updater: operater, UpdateAt: time.Now(), Creator: operater, CreateAt: time.Now(), - //Upstream: input.Upstream, - Method: input.Method, - Path: input.Path, - Match: input.Match, - Service: input.Service, - Team: input.Team, + Method: input.Methods, + Path: input.Path, + Match: input.Match, + Service: input.Service, + Team: input.Team, } err = i.apiInfoStore.Save(ctx, ev) if err != nil { @@ -235,29 +207,35 @@ func (i *imlAPIService) ListProxyCommit(ctx context.Context, commitId ...string) return i.proxyCommitService.List(ctx, commitId...) } -func (i *imlAPIService) ListDocumentCommit(ctx context.Context, commitId ...string) ([]*commit.Commit[Document], error) { - return i.documentCommitService.List(ctx, commitId...) -} - func (i *imlAPIService) CountByService(ctx context.Context, service string) (int64, error) { return i.store.CountWhere(ctx, map[string]interface{}{ "service": service, }) } -func (i *imlAPIService) Exist(ctx context.Context, aid string, a *ExistAPI) error { - t, err := i.store.First(ctx, map[string]interface{}{ - "method": a.Method, - "path": a.Path, +func (i *imlAPIService) Exist(ctx context.Context, aid string, a *Exist) error { + list, err := i.store.Search(ctx, "", map[string]interface{}{ + "path": a.Path, }) if err != nil { - if !errors.Is(err, gorm.ErrRecordNotFound) { - return err - } - return nil + return err } - if t.UUID != aid { - return fmt.Errorf("method(%s),path(%s) is exist", a.Method, a.Path) + existMethodMap := make(map[string]struct{}) + for _, t := range list { + if len(t.Method) == 0 { + if t.UUID != aid { + return fmt.Errorf("method(%v),path(%s) is exist", a.Methods, a.Path) + } + continue + } + for _, m := range t.Method { + existMethodMap[m] = struct{}{} + } + } + for _, m := range a.Methods { + if _, ok := existMethodMap[m]; ok { + return fmt.Errorf("method(%s),path(%s) is exist", m, a.Path) + } } return nil } @@ -269,45 +247,27 @@ func (i *imlAPIService) ListForService(ctx context.Context, serviceId string) ([ } return utils.SliceToSlice(list, FromEntity), nil } -func (i *imlAPIService) listForService(ctx context.Context, serviceId string, isDelete bool) ([]*api.Api, error) { +func (i *imlAPIService) listForService(ctx context.Context, serviceId string, isDelete bool) ([]*api.API, error) { return i.store.ListQuery(ctx, "service=? and is_delete=?", []interface{}{serviceId, isDelete}, "id") } func (i *imlAPIService) ListLatestCommitProxy(ctx context.Context, apiUUID ...string) ([]*commit.Commit[Proxy], error) { - + return i.proxyCommitService.ListLatest(ctx, apiUUID...) - -} -func (i *imlAPIService) ListLatestCommitDocument(ctx context.Context, apiUUID ...string) ([]*commit.Commit[Document], error) { - - return i.documentCommitService.ListLatest(ctx, apiUUID...) + } func (i *imlAPIService) LatestProxy(ctx context.Context, aid string) (*commit.Commit[Proxy], error) { - - return i.proxyCommitService.Latest(ctx, aid) -} -func (i *imlAPIService) LatestDocument(ctx context.Context, aid string) (*commit.Commit[Document], error) { - - return i.documentCommitService.Latest(ctx, aid) + return i.proxyCommitService.Latest(ctx, aid) } func (i *imlAPIService) GetProxyCommit(ctx context.Context, commitId string) (*commit.Commit[Proxy], error) { return i.proxyCommitService.Get(ctx, commitId) } -func (i *imlAPIService) GetDocumentCommit(ctx context.Context, commitId string) (*commit.Commit[Document], error) { - return i.documentCommitService.Get(ctx, commitId) -} - func (i *imlAPIService) SaveProxy(ctx context.Context, aid string, data *Proxy) error { - - return i.proxyCommitService.Save(ctx, aid, data) -} -func (i *imlAPIService) SaveDocument(ctx context.Context, aid string, data *Document) error { - - return i.documentCommitService.Save(ctx, aid, data) + return i.proxyCommitService.Save(ctx, aid, data) } func (i *imlAPIService) GetLabels(ctx context.Context, ids ...string) map[string]string { @@ -324,9 +284,9 @@ func (i *imlAPIService) GetLabels(ctx context.Context, ids ...string) map[string } func (i *imlAPIService) OnComplete() { - i.IServiceGet = universally.NewGetSoftDelete[API, api.Api](i.store, FromEntity) - - i.IServiceDelete = universally.NewSoftDelete[api.Api](i.store) - + i.IServiceGet = universally.NewGetSoftDelete[API, api.API](i.store, FromEntity) + + i.IServiceDelete = universally.NewSoftDelete[api.API](i.store) + auto.RegisterService("api", i) } diff --git a/service/api/model.go b/service/api/model.go index 08f314c1..60a0d572 100644 --- a/service/api/model.go +++ b/service/api/model.go @@ -2,21 +2,22 @@ package api import ( "time" - + "github.com/APIParkLab/APIPark/model/plugin_model" - + "github.com/APIParkLab/APIPark/stores/api" ) type API struct { - UUID string - Service string - Team string - Creator string - Method string - Path string - CreateAt time.Time - IsDelete bool + UUID string + Service string + Team string + Creator string + Method []string + Path string + Protocols []string + CreateAt time.Time + IsDelete bool } type Info struct { @@ -29,45 +30,71 @@ type Info struct { Team string Creator string Updater string - Upstream string - Method string + Methods []string + Protocols []string Path string Match string + Disable bool } -func FromEntity(e *api.Api) *API { +func FromEntity(e *api.API) *API { + return &API{ - UUID: e.UUID, - CreateAt: e.CreateAt, - IsDelete: e.IsDelete != 0, - Service: e.Service, - Team: e.Team, - Creator: e.Creator, - Method: e.Method, - Path: e.Path, + UUID: e.UUID, + CreateAt: e.CreateAt, + IsDelete: e.IsDelete != 0, + Service: e.Service, + Team: e.Team, + Creator: e.Creator, + Method: e.Method, + Path: e.Path, + Protocols: e.Protocol, } } -type CreateAPI struct { +func FromEntityInfo(e *api.Info) *Info { + return &Info{ + UUID: e.UUID, + Name: e.Name, + Description: e.Description, + CreateAt: e.CreateAt, + UpdateAt: e.UpdateAt, + Service: e.Service, + Team: e.Team, + Creator: e.Creator, + Updater: e.Updater, + Methods: e.Method, + Protocols: e.Protocol, + Path: e.Path, + Match: e.Match, + Disable: e.Disable, + } +} + +type Create struct { UUID string - Name string Description string Service string Team string - Method string + Methods []string + Protocols []string + Disable bool Path string Match string } -type EditAPI struct { - Name *string - Upstream *string +type Edit struct { Description *string + Methods *[]string + Protocols *[]string + Disable *bool + Path *string + Match *string } -type ExistAPI struct { - Path string - Method string +type Exist struct { + Path string + Methods []string } type Document struct { @@ -77,6 +104,16 @@ type PluginSetting struct { Disable bool `json:"disable"` Config plugin_model.ConfigType `json:"config"` } + +type Request struct { + //ID string `json:"id"` + Path string `json:"path"` + Methods []string `json:"methods"` + Protocols []string `json:"protocols"` + Match string `json:"match"` + Disable bool `json:"disable"` +} + type Proxy struct { Path string `json:"path"` Timeout int `json:"timeout"` diff --git a/service/api/service.go b/service/api/service.go index 61c36231..8504887d 100644 --- a/service/api/service.go +++ b/service/api/service.go @@ -3,9 +3,9 @@ package api import ( "context" "reflect" - + "github.com/APIParkLab/APIPark/service/universally/commit" - + "github.com/APIParkLab/APIPark/service/universally" "github.com/eolinker/go-common/autowire" ) @@ -15,23 +15,25 @@ type IAPIService interface { universally.IServiceDelete CountByService(ctx context.Context, service string) (int64, error) CountMapByService(ctx context.Context, service ...string) (map[string]int64, error) - Exist(ctx context.Context, aid string, api *ExistAPI) error + Exist(ctx context.Context, aid string, api *Exist) error ListForService(ctx context.Context, serviceId string) ([]*API, error) GetInfo(ctx context.Context, aid string) (*Info, error) ListInfo(ctx context.Context, aids ...string) ([]*Info, error) ListInfoForService(ctx context.Context, serviceId string) ([]*Info, error) + ListLatestCommitRequest(ctx context.Context, aid ...string) ([]*commit.Commit[Request], error) ListLatestCommitProxy(ctx context.Context, aid ...string) ([]*commit.Commit[Proxy], error) - ListLatestCommitDocument(ctx context.Context, aid ...string) ([]*commit.Commit[Document], error) + LatestRequest(ctx context.Context, aid string) (*commit.Commit[Request], error) LatestProxy(ctx context.Context, aid string) (*commit.Commit[Proxy], error) - LatestDocument(ctx context.Context, aid string) (*commit.Commit[Document], error) GetProxyCommit(ctx context.Context, commitId string) (*commit.Commit[Proxy], error) ListProxyCommit(ctx context.Context, commitId ...string) ([]*commit.Commit[Proxy], error) - GetDocumentCommit(ctx context.Context, commitId string) (*commit.Commit[Document], error) - ListDocumentCommit(ctx context.Context, commitId ...string) ([]*commit.Commit[Document], error) SaveProxy(ctx context.Context, aid string, data *Proxy) error - SaveDocument(ctx context.Context, aid string, data *Document) error - Save(ctx context.Context, id string, model *EditAPI) error - Create(ctx context.Context, input *CreateAPI) (err error) + + GetRequestCommit(ctx context.Context, commitId string) (*commit.Commit[Request], error) + ListRequestCommit(ctx context.Context, commitId ...string) ([]*commit.Commit[Request], error) + SaveRequest(ctx context.Context, aid string, data *Request) error + + Save(ctx context.Context, id string, model *Edit) error + Create(ctx context.Context, input *Create) (err error) } var ( @@ -42,7 +44,8 @@ func init() { autowire.Auto[IAPIService](func() reflect.Value { return reflect.ValueOf(new(imlAPIService)) }) - + commit.InitCommitWithKeyService[Proxy]("api", string(HistoryProxy)) - commit.InitCommitWithKeyService[Document]("api", string(HistoryDocument)) + commit.InitCommitWithKeyService[Request]("api", string(HistoryRequest)) + } diff --git a/service/monitor/service.go b/service/monitor/service.go index 159ba469..ed8f8566 100644 --- a/service/monitor/service.go +++ b/service/monitor/service.go @@ -12,7 +12,7 @@ type IMonitorService interface { // Get 获取监控配置 Get(ctx context.Context, id string) (*Monitor, error) // MapByCluster 获取监控配置 - //MapByCluster(ctx context.Context, clusterIds ...string) (map[string]*Monitor, error) + MapByCluster(ctx context.Context, clusterIds ...string) (map[string]*Monitor, error) GetByCluster(ctx context.Context, clusterId string) (*Monitor, error) // Save 保存监控配置 Save(ctx context.Context, monitor *SaveMonitor) error diff --git a/service/release/commit.go b/service/release/commit.go index 5e61526d..fb8ca5c8 100644 --- a/service/release/commit.go +++ b/service/release/commit.go @@ -4,6 +4,8 @@ type CommitType = string const ( CommitApiDocument CommitType = "api_doc" + CommitApiRequest CommitType = "api_request" CommitUpstream CommitType = "upstream" CommitApiProxy CommitType = "api_proxy" + CommitServiceDoc CommitType = "service_doc" ) diff --git a/service/release/iml.go b/service/release/iml.go index 46bc19a0..2b8705ba 100644 --- a/service/release/iml.go +++ b/service/release/iml.go @@ -4,7 +4,7 @@ import ( "context" "errors" "time" - + "github.com/APIParkLab/APIPark/service/api" "github.com/APIParkLab/APIPark/service/universally/commit" "github.com/APIParkLab/APIPark/service/upstream" @@ -28,26 +28,48 @@ type imlReleaseService struct { releaseRuntime release.IReleaseRuntime `autowired:""` } -func (s *imlReleaseService) Completeness(partitions []string, apis []string, proxyCommits []*commit.Commit[api.Proxy], documentCommits []*commit.Commit[api.Document], upstreamCommits []*commit.Commit[upstream.Config]) bool { - +func (s *imlReleaseService) GetRunningApiDocCommits(ctx context.Context, serviceIds ...string) ([]string, error) { + w := make(map[string]interface{}) + if len(serviceIds) > 0 { + w["service"] = serviceIds + } + runnings, err := s.releaseRuntime.List(ctx, w) + if err != nil { + return nil, err + } + apiDocCommits := make([]string, 0, len(runnings)) + for _, running := range runnings { + commits, err := s.getCommitByType(ctx, running.Release, CommitApiDocument, running.Service) + if err != nil { + return nil, err + } + if len(commits) == 0 { + continue + } + apiDocCommits = append(apiDocCommits, commits[0].Commit) + } + return apiDocCommits, nil +} + +func (s *imlReleaseService) Completeness(partitions []string, apis []string, apiRequestCommits []*commit.Commit[api.Request], proxyCommits []*commit.Commit[api.Proxy], upstreamCommits []*commit.Commit[upstream.Config]) bool { + + requests := utils.SliceToMap(apiRequestCommits, func(o *commit.Commit[api.Request]) string { + return o.Target + }) proxys := utils.SliceToMap(proxyCommits, func(o *commit.Commit[api.Proxy]) string { return o.Target }) - - documents := utils.SliceToMap(documentCommits, func(o *commit.Commit[api.Document]) string { - return o.Target - }) + for _, aid := range apis { - _, has := proxys[aid] + _, has := requests[aid] if !has { return false } - - _, has = documents[aid] + _, has = proxys[aid] if !has { return false } - + } upstreamMap := make(map[string]map[string]struct{}) for _, upstreamCommit := range upstreamCommits { @@ -56,7 +78,7 @@ func (s *imlReleaseService) Completeness(partitions []string, apis []string, pro } upstreamMap[upstreamCommit.Target][upstreamCommit.Key] = struct{}{} } - + for _, partition := range partitions { for _, u := range upstreamMap { if _, has := u[partition]; !has { @@ -64,7 +86,7 @@ func (s *imlReleaseService) Completeness(partitions []string, apis []string, pro } } } - + return true } @@ -75,7 +97,7 @@ func (s *imlReleaseService) GetCommits(ctx context.Context, id string) ([]*Proje if err != nil { return nil, err } - + return utils.SliceToSlice(list, func(o *release.Commit) *ProjectCommits { return &ProjectCommits{ Release: o.Release, @@ -112,54 +134,46 @@ func (s *imlReleaseService) GetLabels(ctx context.Context, ids ...string) map[st } func (s *imlReleaseService) GetApiProxyCommit(ctx context.Context, id string, apiUUID string) (string, error) { - commits, err := s.getCommitByType(ctx, id, CommitApiProxy, apiUUID, CommitApiProxy) + commits, err := s.getCommitByType(ctx, id, CommitApiProxy, apiUUID) if err != nil { return "", err } if len(commits) == 0 { return "", errors.New("not found") } - + return commits[0].Commit, nil } -func (s *imlReleaseService) getCommitByType(ctx context.Context, releaseId, t CommitType, target string, key string) ([]*release.Commit, error) { +func (s *imlReleaseService) getCommitByType(ctx context.Context, releaseId, t CommitType, target string) ([]*release.Commit, error) { where := "`release` = ? and `type` = ? and `target` = ?" args := []interface{}{releaseId, t, target} - if len(key) > 0 { - if len(key) == 1 { - where += " and `key` = ?" - args = append(args, key[0]) - } else { - where += " and `key` in ?" - args = append(args, key) - } - } return s.commitStore.ListQuery(ctx, where, args, "") - -} -func (s *imlReleaseService) GetApiDocCommit(ctx context.Context, id string, apiUUID string) (string, error) { - commits, err := s.getCommitByType(ctx, id, CommitApiDocument, apiUUID, CommitApiDocument) - if err != nil { - return "", err - } - if len(commits) == 0 { - return "", errors.New("not found") - } - - return commits[0].Commit, nil + } -func (s *imlReleaseService) GetRunningApiDocCommit(ctx context.Context, service string, apiUUID string) (string, error) { - running, err := s.releaseRuntime.First(ctx, map[string]interface{}{ - "service": service, - }) - if err != nil { - return "", err - } - return s.GetApiDocCommit(ctx, running.Release, apiUUID) - -} +//func (s *imlReleaseService) GetApiDocCommit(ctx context.Context, id string, apiUUID string) (string, error) { +// commits, err := s.getCommitByType(ctx, id, CommitApiDocument, apiUUID, CommitApiDocument) +// if err != nil { +// return "", err +// } +// if len(commits) == 0 { +// return "", errors.New("not found") +// } +// +// return commits[0].Commit, nil +//} + +//func (s *imlReleaseService) GetRunningApiDocCommit(ctx context.Context, service string, apiUUID string) (string, error) { +// running, err := s.releaseRuntime.First(ctx, map[string]interface{}{ +// "service": service, +// }) +// if err != nil { +// return "", err +// } +// return s.GetApiDocCommit(ctx, running.Release, apiUUID) +// +//} func (s *imlReleaseService) GetRunningApiProxyCommit(ctx context.Context, service string, apiUUID string) (string, error) { running, err := s.releaseRuntime.First(ctx, map[string]interface{}{ @@ -264,13 +278,22 @@ func (s *imlReleaseService) SetRunning(ctx context.Context, service string, id s UpdateTime: time.Now(), Operator: operator, }) - + } -func (s *imlReleaseService) CreateRelease(ctx context.Context, service string, version string, remark string, apisProxyCommits, apiDocCommits map[string]string, upstreams map[string]map[string]string) (*Release, error) { +func (s *imlReleaseService) CreateRelease(ctx context.Context, service string, version string, remark string, apiRequestCommit, apisProxyCommits map[string]string, apiDocCommit, serviceDocCommit string, upstreams map[string]map[string]string) (*Release, error) { operator := utils.UserId(ctx) releaseId := uuid.NewString() - commits := make([]*release.Commit, 0, len(apisProxyCommits)+len(apiDocCommits)+len(upstreams)) + commits := make([]*release.Commit, 0, len(apisProxyCommits)+len(upstreams)+2) + for apiId, commitUUID := range apiRequestCommit { + commits = append(commits, &release.Commit{ + Type: CommitApiRequest, + Target: apiId, + Release: releaseId, + Key: CommitApiRequest, + Commit: commitUUID, + }) + } for aid, commitUUID := range apisProxyCommits { commits = append(commits, &release.Commit{ Type: CommitApiProxy, @@ -280,15 +303,7 @@ func (s *imlReleaseService) CreateRelease(ctx context.Context, service string, v Commit: commitUUID, }) } - for apiId, commitUUID := range apiDocCommits { - commits = append(commits, &release.Commit{ - Type: CommitApiDocument, - Target: apiId, - Release: releaseId, - Key: CommitApiDocument, - Commit: commitUUID, - }) - } + for upId, upstreamsByPartition := range upstreams { for partition, commitUUID := range upstreamsByPartition { commits = append(commits, &release.Commit{ @@ -300,6 +315,26 @@ func (s *imlReleaseService) CreateRelease(ctx context.Context, service string, v }) } } + if apiDocCommit != "" { + commits = append(commits, &release.Commit{ + Type: CommitApiDocument, + Target: service, + Release: releaseId, + Key: CommitApiDocument, + Commit: apiDocCommit, + }) + } + + if serviceDocCommit != "" { + commits = append(commits, &release.Commit{ + Type: CommitServiceDoc, + Target: service, + Release: releaseId, + Key: CommitServiceDoc, + Commit: serviceDocCommit, + }) + } + ev := &release.Release{ Id: 0, UUID: releaseId, @@ -317,7 +352,7 @@ func (s *imlReleaseService) CreateRelease(ctx context.Context, service string, v if !ok { return errors.New("version already exists") } - + err := s.releaseStore.Insert(ctx, ev) if err != nil { return err @@ -367,14 +402,14 @@ func (s *imlReleaseService) GetRelease(ctx context.Context, id string) (*Release // } // // df := new(Diff) -// df.Apis = s.DiffApis(ctx, baseApis, targetApis) +// df.Routers = s.DiffApis(ctx, baseApis, targetApis) // df.Upstreams = s.DiffUpstreams(ctx, baseUpstreams, targetUpstreams) // return df, nil //} func (s *imlReleaseService) DeleteRelease(ctx context.Context, id string) error { //todo 判断版本是否有使用中的未完结流程 - + return s.releaseStore.Transaction(ctx, func(ctx context.Context) error { first, err := s.releaseRuntime.First(ctx, map[string]interface{}{ "release": id, @@ -397,7 +432,7 @@ func (s *imlReleaseService) DeleteRelease(ctx context.Context, id string) error } return nil }) - + } func (s *imlReleaseService) List(ctx context.Context, service string) ([]*Release, error) { @@ -408,33 +443,40 @@ func (s *imlReleaseService) List(ctx context.Context, service string) ([]*Releas return utils.SliceToSlice(list, FromEntity), nil } -func (s *imlReleaseService) GetReleaseInfos(ctx context.Context, id string) ([]*APIProxyCommit, []*APIDocumentCommit, []*UpstreamCommit, error) { +func (s *imlReleaseService) GetReleaseInfos(ctx context.Context, id string) ([]*APICommit, []*APICommit, *APICommit, []*UpstreamCommit, *ServiceCommit, error) { commits, err := s.commitStore.List(ctx, map[string]interface{}{ "release": id, }) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, nil, err } - apiProxyCommits := make([]*APIProxyCommit, 0, len(commits)) - apiDocumentCommits := make([]*APIDocumentCommit, 0, len(commits)) + apiRequestCommits := make([]*APICommit, 0, len(commits)) + apiProxyCommits := make([]*APICommit, 0, len(commits)) + var apiDocCommit *APICommit upstreamCommits := make([]*UpstreamCommit, 0, len(commits)) - + var serviceDocCommit *ServiceCommit for _, v := range commits { switch v.Type { + case CommitApiRequest: + apiRequestCommits = append(apiRequestCommits, &APICommit{ + Release: v.Release, + API: v.Target, + Commit: v.Commit, + }) case CommitApiProxy: - apiProxyCommits = append(apiProxyCommits, &APIProxyCommit{ + apiProxyCommits = append(apiProxyCommits, &APICommit{ Release: v.Release, API: v.Target, Commit: v.Commit, }) - + case CommitApiDocument: - apiDocumentCommits = append(apiDocumentCommits, &APIDocumentCommit{ + apiDocCommit = &APICommit{ Release: v.Release, API: v.Target, Commit: v.Commit, - }) - + } + case CommitUpstream: upstreamCommits = append(upstreamCommits, &UpstreamCommit{ Release: v.Release, @@ -442,11 +484,17 @@ func (s *imlReleaseService) GetReleaseInfos(ctx context.Context, id string) ([]* Partition: v.Key, Commit: v.Commit, }) + case CommitServiceDoc: + serviceDocCommit = &ServiceCommit{ + Release: v.Release, + Service: v.Target, + Commit: v.Commit, + } } - + } - - return apiProxyCommits, apiDocumentCommits, upstreamCommits, nil + + return apiRequestCommits, apiProxyCommits, apiDocCommit, upstreamCommits, serviceDocCommit, nil } func (s *imlReleaseService) GetRunning(ctx context.Context, service string) (*Release, error) { diff --git a/service/release/model.go b/service/release/model.go index 68e75c04..2d93af74 100644 --- a/service/release/model.go +++ b/service/release/model.go @@ -2,7 +2,7 @@ package release import ( "time" - + "github.com/APIParkLab/APIPark/stores/release" ) @@ -26,16 +26,18 @@ func FromEntity(e *release.Release) *Release { } } -type APIProxyCommit struct { +type APICommit struct { Release string API string Commit string } -type APIDocumentCommit struct { + +type ServiceCommit struct { Release string - API string + Service string Commit string } + type UpstreamCommit struct { Release string Upstream string @@ -52,7 +54,7 @@ type ProjectCommits struct { } //type Diff struct { -// Apis []*APiDiff `json:"apis"` +// Routers []*APiDiff `json:"apis"` // Upstreams []*UpstreamDiff `json:"upstream"` //} diff --git a/service/release/service.go b/service/release/service.go index 3dd254e3..b248d275 100644 --- a/service/release/service.go +++ b/service/release/service.go @@ -3,7 +3,7 @@ package release import ( "context" "reflect" - + "github.com/APIParkLab/APIPark/service/api" "github.com/APIParkLab/APIPark/service/universally/commit" "github.com/APIParkLab/APIPark/service/upstream" @@ -14,26 +14,23 @@ type IReleaseService interface { // GetRelease 获取发布信息 GetRelease(ctx context.Context, id string) (*Release, error) // CreateRelease 创建发布 - CreateRelease(ctx context.Context, service string, version string, remark string, apisProxyCommits, apiDocCommits map[string]string, upstreams map[string]map[string]string) (*Release, error) + CreateRelease(ctx context.Context, service string, version string, remark string, apiRequestCommit, apisProxyCommits map[string]string, apiDocCommits, serviceDocCommits string, upstreams map[string]map[string]string) (*Release, error) // DeleteRelease 删除发布 DeleteRelease(ctx context.Context, id string) error List(ctx context.Context, service string) ([]*Release, error) - GetApiProxyCommit(ctx context.Context, id string, apiUUID string) (string, error) - GetApiDocCommit(ctx context.Context, id string, apiUUID string) (string, error) - GetReleaseInfos(ctx context.Context, id string) ([]*APIProxyCommit, []*APIDocumentCommit, []*UpstreamCommit, error) + GetReleaseInfos(ctx context.Context, id string) ([]*APICommit, []*APICommit, *APICommit, []*UpstreamCommit, *ServiceCommit, error) GetCommits(ctx context.Context, id string) ([]*ProjectCommits, error) - - GetRunningApiDocCommit(ctx context.Context, service string, apiUUID string) (string, error) + GetRunningApiDocCommits(ctx context.Context, serviceIds ...string) ([]string, error) GetRunningApiProxyCommit(ctx context.Context, service string, apiUUID string) (string, error) - Completeness(partitions []string, apis []string, proxyCommits []*commit.Commit[api.Proxy], documentCommits []*commit.Commit[api.Document], upstreamCommits []*commit.Commit[upstream.Config]) bool - + Completeness(partitions []string, apis []string, requestCommits []*commit.Commit[api.Request], proxyCommits []*commit.Commit[api.Proxy], upstreamCommits []*commit.Commit[upstream.Config]) bool + // GetRunning gets the running release with the given service. // // ctx: the context // service: the service name // Return type(s): *Release, error GetRunning(ctx context.Context, service string) (*Release, error) - + SetRunning(ctx context.Context, service string, id string) error CheckNewVersion(ctx context.Context, service string, version string) (bool, error) } diff --git a/service/service-doc/iml.go b/service/service-doc/iml.go index aedc1502..ddfcf67a 100644 --- a/service/service-doc/iml.go +++ b/service/service-doc/iml.go @@ -3,35 +3,97 @@ package service_doc import ( "context" "errors" + "github.com/APIParkLab/APIPark/service/universally/commit" "time" - + "github.com/eolinker/go-common/utils" "gorm.io/gorm" - + "github.com/APIParkLab/APIPark/stores/service" ) +var _ IDocService = (*imlDocService)(nil) + type imlDocService struct { - store service.IServiceDocStore `autowired:""` + store service.IServiceDocStore `autowired:""` + commitService commit.ICommitWithKeyService[DocCommit] `autowired:""` +} + +func (i *imlDocService) GetDocCommit(ctx context.Context, commitId string) (*commit.Commit[DocCommit], error) { + return i.commitService.Get(ctx, commitId) +} + +func (i *imlDocService) ListLatestDocCommit(ctx context.Context, serviceIds ...string) ([]*commit.Commit[DocCommit], error) { + return i.commitService.ListLatest(ctx, serviceIds...) +} + +func (i *imlDocService) ListDocCommit(ctx context.Context, commitIds ...string) ([]*commit.Commit[DocCommit], error) { + return i.commitService.List(ctx, commitIds...) +} + +func (i *imlDocService) LatestDocCommit(ctx context.Context, serviceId string) (*commit.Commit[DocCommit], error) { + return i.commitService.Latest(ctx, serviceId) +} + +func (i *imlDocService) CommitDoc(ctx context.Context, serviceId string, data *Doc) error { + return i.commitService.Save(ctx, serviceId, &DocCommit{Content: data.Doc}) +} + +func (i *imlDocService) List(ctx context.Context, sids ...string) ([]*Doc, error) { + w := map[string]interface{}{} + if len(sids) > 0 { + w["sid"] = sids + } + + list, err := i.store.List(ctx, w) + if err != nil { + return nil, err + } + return utils.SliceToSlice(list, func(d *service.Doc) *Doc { + return &Doc{ + ID: d.Sid, + Creator: d.Creator, + Updater: d.Updater, + Doc: d.Doc, + UpdateTime: d.UpdateAt, + CreateTime: d.CreateAt, + } + }), nil +} + +func (i *imlDocService) Map(ctx context.Context, sids ...string) (map[string]*Doc, error) { + w := map[string]interface{}{} + if len(sids) > 0 { + w["sid"] = sids + } + + list, err := i.store.List(ctx, w) + if err != nil { + return nil, err + } + return utils.SliceToMapO(list, func(d *service.Doc) (string, *Doc) { + return d.Sid, &Doc{ + ID: d.Sid, + Creator: d.Creator, + Updater: d.Updater, + Doc: d.Doc, + UpdateTime: d.UpdateAt, + CreateTime: d.CreateAt, + } + }), nil } func (i *imlDocService) Save(ctx context.Context, input *SaveDoc) error { - info, err := i.Get(ctx, input.Sid) + info, err := i.store.First(ctx, map[string]interface{}{"sid": input.Sid}) if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { return err } userID := utils.UserId(ctx) if info != nil { - _, err = i.store.Update(ctx, &service.Doc{ - Id: info.ID, - Sid: input.Sid, - Doc: input.Doc, - CreateAt: info.CreateTime, - UpdateAt: time.Now(), - Creator: info.Creator, - Updater: userID, - }) - return err + info.Doc = input.Doc + info.Updater = userID + info.UpdateAt = time.Now() + return i.store.Save(ctx, info) } return i.store.Insert(ctx, &service.Doc{ Sid: input.Sid, @@ -49,8 +111,7 @@ func (i *imlDocService) Get(ctx context.Context, sid string) (*Doc, error) { return nil, err } return &Doc{ - ID: doc.Id, - DocID: doc.Sid, + ID: doc.Sid, Creator: doc.Creator, Updater: doc.Updater, Doc: doc.Doc, diff --git a/service/service-doc/model.go b/service/service-doc/model.go index ef0dd89e..6d09bc54 100644 --- a/service/service-doc/model.go +++ b/service/service-doc/model.go @@ -3,9 +3,7 @@ package service_doc import "time" type Doc struct { - ID int64 - DocID string - Name string + ID string Creator string Updater string Doc string @@ -17,3 +15,7 @@ type SaveDoc struct { Sid string Doc string } + +type DocCommit struct { + Content string `json:"content"` +} diff --git a/service/service-doc/service.go b/service/service-doc/service.go index 451182d9..109bae1f 100644 --- a/service/service-doc/service.go +++ b/service/service-doc/service.go @@ -2,6 +2,7 @@ package service_doc import ( "context" + "github.com/APIParkLab/APIPark/service/universally/commit" "reflect" "github.com/eolinker/go-common/autowire" @@ -10,10 +11,24 @@ import ( type IDocService interface { Get(ctx context.Context, sid string) (*Doc, error) Save(ctx context.Context, input *SaveDoc) error + List(ctx context.Context, sids ...string) ([]*Doc, error) + Map(ctx context.Context, sids ...string) (map[string]*Doc, error) + IDocCommitService +} + +type IDocCommitService interface { + CommitDoc(ctx context.Context, serviceId string, data *Doc) error + GetDocCommit(ctx context.Context, commitId string) (*commit.Commit[DocCommit], error) + // LatestDocCommit 获取最新文档 + LatestDocCommit(ctx context.Context, serviceId string) (*commit.Commit[DocCommit], error) + ListLatestDocCommit(ctx context.Context, serviceIds ...string) ([]*commit.Commit[DocCommit], error) + ListDocCommit(ctx context.Context, commitIds ...string) ([]*commit.Commit[DocCommit], error) } func init() { autowire.Auto[IDocService](func() reflect.Value { return reflect.ValueOf(new(imlDocService)) }) + + commit.InitCommitWithKeyService[DocCommit]("service", "service_doc") } diff --git a/service/service_diff/diff.go b/service/service_diff/diff.go index 1ecd828d..2b579ce1 100644 --- a/service/service_diff/diff.go +++ b/service/service_diff/diff.go @@ -19,10 +19,12 @@ type Status struct { } type ApiDiff struct { - APi string `json:"api,omitempty"` - Upstream string `json:"upstream,omitempty"` - Name string `json:"name,omitempty"` - Method string `json:"method,omitempty"` + APi string `json:"api,omitempty"` + //Upstream string `json:"upstream,omitempty"` + //Name string `json:"name,omitempty"` + Method []string `json:"method,omitempty"` + Protocol []string `json:"protocol,omitempty"` + Disable bool `json:"disable,omitempty"` Path string `json:"path,omitempty"` Change ChangeType `json:"change,omitempty"` Status Status `json:"status,omitempty"` diff --git a/service/subscribe/iml.go b/service/subscribe/iml.go index 9d18f562..dd3fd460 100644 --- a/service/subscribe/iml.go +++ b/service/subscribe/iml.go @@ -186,6 +186,7 @@ func (i *imlSubscribeService) uniquestHandler(t *CreateSubscribe) []map[string]i func (i *imlSubscribeService) createEntityHandler(t *CreateSubscribe) *subscribe.Subscribe { return &subscribe.Subscribe{ UUID: t.Uuid, + Name: t.Uuid, Application: t.Application, Service: t.Service, From: t.From, @@ -196,16 +197,9 @@ func (i *imlSubscribeService) createEntityHandler(t *CreateSubscribe) *subscribe } func (i *imlSubscribeService) updateHandler(e *subscribe.Subscribe, t *UpdateSubscribe) { - //if t.Approver != nil { - // e.Approver = *t.Approver - //} if t.ApplyStatus != nil { e.ApplyStatus = *t.ApplyStatus } - - //if t.ApplyID != nil { - // e.ApplyID = *t.ApplyID - //} } var ( @@ -274,6 +268,7 @@ func (i *imlSubscribeApplyService) createEntityHandler(t *CreateApply) *subscrib now := time.Now() return &subscribe.Apply{ Uuid: t.Uuid, + Name: t.Uuid, Service: t.Service, Team: t.Team, Application: t.Application, diff --git a/service/tag/iml.go b/service/tag/iml.go index dd489f57..a675ff43 100644 --- a/service/tag/iml.go +++ b/service/tag/iml.go @@ -3,12 +3,12 @@ package tag import ( "context" "time" - + "github.com/eolinker/go-common/auto" "github.com/eolinker/go-common/utils" - + "github.com/APIParkLab/APIPark/stores/tag" - + "github.com/APIParkLab/APIPark/service/universally" ) @@ -23,6 +23,26 @@ type imlTagService struct { universally.IServiceCreate[CreateTag] } +func (i *imlTagService) Map(ctx context.Context, ids ...string) (map[string]*Tag, error) { + w := make(map[string]interface{}) + if len(ids) > 0 { + w["uuid"] = ids + } + + list, err := i.store.List(ctx, w) + if err != nil { + return nil, err + } + return utils.SliceToMapO(list, func(i *tag.Tag) (string, *Tag) { + return i.UUID, &Tag{ + Id: i.UUID, + Name: i.Name, + CreateTime: i.CreateAt, + UpdateTime: i.UpdateAt, + } + }), nil +} + func (i *imlTagService) GetLabels(ctx context.Context, ids ...string) map[string]string { if len(ids) == 0 { return nil diff --git a/service/tag/service.go b/service/tag/service.go index da184959..8ce8b6b6 100644 --- a/service/tag/service.go +++ b/service/tag/service.go @@ -1,8 +1,9 @@ package tag import ( + "context" "reflect" - + "github.com/APIParkLab/APIPark/service/universally" "github.com/eolinker/go-common/autowire" ) @@ -11,6 +12,7 @@ type ITagService interface { universally.IServiceGet[Tag] universally.IServiceDelete universally.IServiceCreate[CreateTag] + Map(ctx context.Context, ids ...string) (map[string]*Tag, error) } func init() { diff --git a/service/universally/get.go b/service/universally/get.go index 63aaabc5..a9802b2f 100644 --- a/service/universally/get.go +++ b/service/universally/get.go @@ -21,7 +21,7 @@ type IServiceGet[T any] interface { Search(ctx context.Context, keyword string, condition map[string]interface{}, sortRule ...string) ([]*T, error) Count(ctx context.Context, keyword string, condition map[string]interface{}) (int64, error) - CountByGroup(ctx context.Context, keyword string, condition map[string]interface{}, groupBy string) (map[string]int64, error) + //CountByGroup(ctx context.Context, keyword string, condition map[string]interface{}, groupBy string) (map[string]int64, error) SearchByPage(ctx context.Context, keyword string, condition map[string]interface{}, page int, pageSize int, sortRule ...string) ([]*T, int64, error) } @@ -45,7 +45,7 @@ func (s *imlServiceGet[T, E]) Get(ctx context.Context, uuid string) (*T, error) return nil, err } if v == nil || errors.Is(err, gorm.ErrRecordNotFound) { - return nil, errors.New("not found") + return nil, gorm.ErrRecordNotFound } return s.toModelHandler(v), nil diff --git a/service/upstream/iml.go b/service/upstream/iml.go index 343347de..9d5ee800 100644 --- a/service/upstream/iml.go +++ b/service/upstream/iml.go @@ -4,7 +4,7 @@ import ( "context" "errors" "time" - + "github.com/APIParkLab/APIPark/service/universally/commit" "github.com/APIParkLab/APIPark/stores/upstream" "github.com/eolinker/go-common/autowire" @@ -21,14 +21,43 @@ type imlUpstreamService struct { commitService commit.ICommitService[Config] `autowired:""` } +func (i *imlUpstreamService) List(ctx context.Context, serviceIds ...string) ([]*Upstream, error) { + w := make(map[string]interface{}) + if len(serviceIds) > 0 { + w["service"] = serviceIds + } + + upstreams, err := i.store.List(ctx, w) + if err != nil { + return nil, err + } + return utils.SliceToSlice(upstreams, func(u *upstream.Upstream) *Upstream { + return &Upstream{ + Item: &Item{ + UUID: u.UUID, + Service: u.Service, + Team: u.Team, + Remark: u.Remark, + Creator: u.Creator, + Updater: u.Updater, + CreateTime: u.CreateAt, + UpdateTime: u.UpdateAt, + }, + } + }), nil +} + func (i *imlUpstreamService) ListCommit(ctx context.Context, uuid ...string) ([]*commit.Commit[Config], error) { return i.commitService.List(ctx, uuid...) } -func (i *imlUpstreamService) ListLatestCommit(ctx context.Context, project string) ([]*commit.Commit[Config], error) { - upstreams, err := i.store.List(ctx, map[string]interface{}{ - "project": project, - }) +func (i *imlUpstreamService) ListLatestCommit(ctx context.Context, serviceIds ...string) ([]*commit.Commit[Config], error) { + w := make(map[string]interface{}) + if len(serviceIds) > 0 { + w["service"] = serviceIds + } + + upstreams, err := i.store.List(ctx, w) if err != nil { return nil, err } @@ -39,16 +68,16 @@ func (i *imlUpstreamService) ListLatestCommit(ctx context.Context, project strin return u.UUID }) return i.commitService.ListLatest(ctx, targetId...) - + } func (i *imlUpstreamService) GetCommit(ctx context.Context, uuid string) (*commit.Commit[Config], error) { return i.commitService.Get(ctx, uuid) } -func (i *imlUpstreamService) LatestCommit(ctx context.Context, uid string, partition string) (*commit.Commit[Config], error) { - - return i.commitService.Latest(ctx, uid, partition) +func (i *imlUpstreamService) LatestCommit(ctx context.Context, uid string, clusterId string) (*commit.Commit[Config], error) { + + return i.commitService.Latest(ctx, uid, clusterId) } func (i *imlUpstreamService) SaveCommit(ctx context.Context, uid string, partition string, cfg *Config) error { @@ -64,11 +93,11 @@ func (i *imlUpstreamService) Get(ctx context.Context, id string) (*Upstream, err if err != nil { return nil, err } - + return &Upstream{ Item: &Item{ UUID: t.UUID, - Project: t.Project, + Service: t.Service, Team: t.Team, Remark: t.Remark, Creator: t.Creator, @@ -85,7 +114,7 @@ func (i *imlUpstreamService) Save(ctx context.Context, u *SaveUpstream) error { return i.store.Save(ctx, &upstream.Upstream{ UUID: u.UUID, Name: u.Name, - Project: u.Project, + Service: u.Service, Team: u.Team, Remark: u.Remark, Creator: userId, diff --git a/service/upstream/model.go b/service/upstream/model.go index 5e9ee871..c1c9a4a0 100644 --- a/service/upstream/model.go +++ b/service/upstream/model.go @@ -8,7 +8,7 @@ type Item struct { UUID string Name string Type string - Project string + Service string Team string Creator string Updater string @@ -24,10 +24,9 @@ type Upstream struct { type SaveUpstream struct { UUID string Name string - Project string + Service string Team string - //Type string - Remark string + Remark string } type ProxyHeader struct { diff --git a/service/upstream/service.go b/service/upstream/service.go index 404aac16..01e48a9a 100644 --- a/service/upstream/service.go +++ b/service/upstream/service.go @@ -3,7 +3,7 @@ package upstream import ( "context" "reflect" - + "github.com/APIParkLab/APIPark/service/universally/commit" "github.com/eolinker/go-common/autowire" ) @@ -12,8 +12,9 @@ type IUpstreamService interface { Get(ctx context.Context, id string) (*Upstream, error) Save(ctx context.Context, upstream *SaveUpstream) error Delete(ctx context.Context, id string) error - LatestCommit(ctx context.Context, uid string, partition string) (*commit.Commit[Config], error) - ListLatestCommit(ctx context.Context, project string) ([]*commit.Commit[Config], error) + List(ctx context.Context, serviceIds ...string) ([]*Upstream, error) + LatestCommit(ctx context.Context, uid string, clusterId string) (*commit.Commit[Config], error) + ListLatestCommit(ctx context.Context, serviceIds ...string) ([]*commit.Commit[Config], error) SaveCommit(ctx context.Context, uid string, partition string, cfg *Config) error GetCommit(ctx context.Context, uuid string) (*commit.Commit[Config], error) ListCommit(ctx context.Context, uuid ...string) ([]*commit.Commit[Config], error) @@ -24,5 +25,5 @@ func init() { return reflect.ValueOf(new(imlUpstreamService)) }) commit.InitCommitService[Config]("upstream") - + } diff --git a/stores/api/api.go b/stores/api/api.go index 9713f47d..c8cb4d7a 100644 --- a/stores/api/api.go +++ b/stores/api/api.go @@ -8,11 +8,19 @@ import ( ) type IApiBaseStore interface { - store.ISearchStore[Api] + store.ISearchStore[API] } type IAPIInfoStore store.IBaseStore[Info] type imlApiBaseStore struct { - store.SearchStoreSoftDelete[Api] + store.SearchStoreSoftDelete[API] +} + +type imlAPIDocStore struct { + store.Store[Doc] +} + +type IAPIDocStore interface { + store.IBaseStore[Doc] } func init() { @@ -23,4 +31,7 @@ func init() { autowire.Auto[IAPIInfoStore](func() reflect.Value { return reflect.ValueOf(new(store.Store[Info])) }) + autowire.Auto[IAPIDocStore](func() reflect.Value { + return reflect.ValueOf(new(imlAPIDocStore)) + }) } diff --git a/stores/api/model.go b/stores/api/model.go index c245b213..ed1dca17 100644 --- a/stores/api/model.go +++ b/stores/api/model.go @@ -2,7 +2,7 @@ package api import "time" -type Api struct { +type API struct { Id int64 `gorm:"column:id;type:BIGINT(20);AUTO_INCREMENT;NOT NULL;comment:id;primary_key;comment:主键ID;"` UUID string `gorm:"type:varchar(36);not null;column:uuid;uniqueIndex:uuid;comment:UUID;"` Name string `gorm:"type:varchar(100);not null;column:name;comment:name"` @@ -12,7 +12,8 @@ type Api struct { Creator string `gorm:"size:36;not null;column:creator;comment:创建人;index:creator" aovalue:"creator"` // 创建人 CreateAt time.Time `gorm:"type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP;column:create_at;comment:创建时间"` IsDelete int `gorm:"type:tinyint(1);not null;column:is_delete;comment:是否删除 0:未删除 1:已删除"` - Method string `gorm:"size:36;not null;column:method;comment:请求方法"` + Method []string `gorm:"size:36;not null;column:method;comment:请求方法;serializer:json"` + Protocol []string `gorm:"type:text;not null;column:protocol;comment:协议;serializer:json"` Path string `gorm:"size:512;not null;column:path;comment:请求路径"` } type Info struct { @@ -22,13 +23,15 @@ type Info struct { Description string `gorm:"size:255;not null;column:description;comment:description"` Service string `gorm:"size:36;not null;column:service;comment:服务;index:service"` Team string `gorm:"size:36;not null;column:team;comment:团队;index:team"` // 团队id - Method string `gorm:"size:36;not null;column:method;comment:请求方法"` + Method []string `gorm:"size:36;not null;column:method;comment:请求方法;serializer:json" ` Path string `gorm:"size:512;not null;column:path;comment:请求路径"` + Protocol []string `gorm:"type:text;null;column:protocol;comment:协议;serializer:json"` Match string `gorm:"type:text;null;column:match;comment:匹配规则"` Creator string `gorm:"size:36;not null;column:creator;comment:创建人;index:creator" aovalue:"creator"` // 创建人 CreateAt time.Time `gorm:"type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP;column:create_at;comment:创建时间"` Updater string `gorm:"size:36;not null;column:updater;comment:更新人;index:updater" aovalue:"updater"` // 更新人 UpdateAt time.Time `gorm:"type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP;column:update_at;comment:更新时间"` + Disable bool `gorm:"type:tinyint(1);not null;column:disable;comment:是否禁用 0:否 1:是"` } func (i *Info) TableName() string { @@ -39,9 +42,27 @@ func (i *Info) IdValue() int64 { return i.Id } -func (a *Api) IdValue() int64 { +func (a *API) IdValue() int64 { return a.Id } -func (a *Api) TableName() string { +func (a *API) TableName() string { return "api" } + +type Doc struct { + Id int64 `gorm:"column:id;type:BIGINT(20);AUTO_INCREMENT;NOT NULL;comment:id;primary_key;comment:主键ID;"` + UUID string `gorm:"type:varchar(36);not null;column:uuid;uniqueIndex:uuid;comment:UUID;"` + Service string `gorm:"size:36;not null;column:service;comment:服务;index:service"` + Content string `gorm:"type:text;null;column:content;comment:文档内容"` + Updater string `gorm:"size:36;not null;column:updater;comment:更新人;index:updater" aovalue:"updater"` + UpdateAt time.Time `gorm:"type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP;column:update_at;comment:更新时间"` + APICount int64 `gorm:"type:int(11);not null;column:api_count;comment:接口数量"` +} + +func (i *Doc) TableName() string { + return "api_doc" +} + +func (i *Doc) IdValue() int64 { + return i.Id +} diff --git a/stores/release/release.go b/stores/release/release.go index 8dfadce1..9e23a33c 100644 --- a/stores/release/release.go +++ b/stores/release/release.go @@ -22,7 +22,7 @@ func (r *Release) TableName() string { type Commit struct { Id int64 `gorm:"column:id;type:BIGINT(20);AUTO_INCREMENT;NOT NULL;comment:id;primary_key;comment:主键ID;"` Release string `gorm:"type:varchar(36);not null;column:release;comment:release id; index:release; uniqueIndex:type_release_type_key"` - Type string `gorm:"type:varchar(10);not null;column:type;comment:类型;index:type;uniqueIndex:type_release_type_key"` + Type string `gorm:"type:varchar(30);not null;column:type;comment:类型;index:type;uniqueIndex:type_release_type_key"` Target string `gorm:"type:varchar(36);not null;column:target;comment:目标;index:target;uniqueIndex:type_release_type_key"` Key string `gorm:"type:varchar(36);not null;column:api;comment:api id;uniqueIndex:type_release_type_key"` Commit string `gorm:"type:varchar(36);not null;column:commit;comment:commit;"` diff --git a/stores/subscribe/model.go b/stores/subscribe/model.go index 7b8baa8b..c68dbcc1 100644 --- a/stores/subscribe/model.go +++ b/stores/subscribe/model.go @@ -5,6 +5,7 @@ import "time" type Subscribe struct { Id int64 `gorm:"column:id;type:BIGINT(20);AUTO_INCREMENT;NOT NULL;comment:id;primary_key;comment:主键ID;"` UUID string `gorm:"size:36;not null;column:uuid;comment:uuid;uniqueIndex:uuid;"` + Name string `gorm:"size:36;not null;column:name;comment:名称"` Service string `gorm:"size:36;not null;column:service;comment:服务id;uniqueIndex:unique_subscribe"` Application string `gorm:"size:36;not null;column:application;comment:应用id,项目id,系统id;uniqueIndex:unique_subscribe"` ApplyStatus int `gorm:"type:tinyint(1);not null;column:apply_status;comment:申请状态;index:status;"` @@ -24,7 +25,8 @@ func (s *Subscribe) TableName() string { type Apply struct { Id int64 `gorm:"column:id;type:BIGINT(20);NOT NULL;comment:id;primary_key;comment:主键ID;"` - Uuid string `gorm:"size:36;not null;column:uuid;comment:uuid;uniqueIndex:uuid;"` // uuid + Uuid string `gorm:"size:36;not null;column:uuid;comment:uuid;uniqueIndex:uuid;"` // uuid + Name string `gorm:"size:36;not null;column:name;comment:名称"` Service string `gorm:"size:36;not null;column:service;comment:服务id;index:service"` // 服务id Team string `gorm:"size:36;not null;column:team;comment:团队id;index:team;"` // 团队id Application string `gorm:"size:36;not null;column:application;comment:应用id,项目id,系统id;index:application"` // 订阅应用id diff --git a/stores/universally/commit/commit.go b/stores/universally/commit/commit.go index 898e9a0f..3edfe664 100644 --- a/stores/universally/commit/commit.go +++ b/stores/universally/commit/commit.go @@ -7,7 +7,7 @@ type Commit[H any] struct { UUID string `gorm:"size:36;not null;column:uuid;comment:uuid;uniqueIndex:uuid;"` Target string `gorm:"column:target;type:varchar(36);NOT NULL;comment:目标id;index:target;"` Key string `gorm:"size:36;not null;column:key;comment:类型;index:key;"` - Data *H `gorm:"type:text;not null;column:data;comment:数据;serializer:json"` + Data *H `gorm:"type:text;not null;column:data;comment:数据;charset=utf8mb4;serializer:json"` CreateAt time.Time `gorm:"type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP;column:create_at;comment:创建时间"` Operator string `gorm:"size:36;not null;column:operator;comment:操作人;index:operator;"` } diff --git a/stores/upstream/model.go b/stores/upstream/model.go index 19383c72..e14c55e5 100644 --- a/stores/upstream/model.go +++ b/stores/upstream/model.go @@ -8,7 +8,7 @@ type Upstream struct { Id int64 `gorm:"column:id;type:BIGINT(20);AUTO_INCREMENT;NOT NULL;comment:id;primary_key;comment:主键ID;"` UUID string `gorm:"type:varchar(36);not null;column:uuid;uniqueIndex:uuid;comment:UUID;"` Name string `gorm:"size:255;not null;column:name;comment:名称"` - Project string `gorm:"size:36;not null;column:project;comment:项目;index:project;"` // 项目id + Service string `gorm:"size:36;not null;column:service;comment:项目;index:service;"` // 服务id Team string `gorm:"size:36;not null;column:team;comment:团队;index:team;"` // 团队id Remark string `gorm:"size:255;not null;column:remark;comment:备注"` Creator string `gorm:"size:36;not null;column:creator;comment:创建人;index:creator;"` // 创建人