From 3c599857341f3a7eb77ef098bacc888131a53430 Mon Sep 17 00:00:00 2001 From: Liujian <824010343@qq.com> Date: Fri, 15 Nov 2024 14:35:27 +0800 Subject: [PATCH 01/14] add gitlab-ci --- .gitignore | 3 +- .gitlab-ci.yml | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 .gitlab-ci.yml diff --git a/.gitignore b/.gitignore index 830ed6b3..f4ef62f4 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,4 @@ /.idea/ /config.yml /build/ -/apipark -.gitlab-ci.yml \ No newline at end of file +/apipark \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 00000000..88137b31 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,98 @@ +variables: + PATH: /opt/go-1.21/go/bin/:/opt/node/node/bin/:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin + GOROOT: /opt/go-1.21/go + GOPROXY: https://goproxy.cn + VERSION: $CI_COMMIT_SHORT_SHA + APP: apipark + APP_PRE: ${APP}_${VERSION} + BUILD_DIR: ${APP}-build + DEPLOY_DESC: "DEV 环境" + VIEW_ADDR: http://172.18.166.219:8288 + SAVE_DIR: /opt/${APP} + NODE_OPTIONS: --max_old_space_size=8192 + +stages: + - notice + - prefix + - build + - deploy + - webhook + +feishu-informer: # 飞书回调 + stage: notice + variables: + DIFF_URL: "$CI_MERGE_REQUEST_PROJECT_URL/-/merge_requests/$CI_MERGE_REQUEST_IID/diffs" + rules: + - if: $CI_PIPELINE_SOURCE=="merge_request_event" && $CI_COMMIT_BRANCH =~ "main" + script: + - echo "merge request" + - | + curl -X POST -H "Content-Type: application/json" \ + -d "{\"msg_type\":\"text\",\"content\":{\"text\":\"项目:${CI_PROJECT_NAME}\\n提交人:${GITLAB_USER_NAME}\\n提交信息:${CI_MERGE_REQUEST_TITLE}\\n合并分支信息:${CI_MERGE_REQUEST_SOURCE_BRANCH_NAME} -> ${CI_MERGE_REQUEST_TARGET_BRANCH_NAME}\\n差异性地址:${DIFF_URL}\\n请及时review代码\"}}" \ + https://open.feishu.cn/open-apis/bot/v2/hook/1c334752-2874-41a1-8f1b-3060f2d46b6c + +prebuild: + stage: prefix + rules: + - if: $CI_COMMIT_BRANCH == "main" + script: + - echo "prebuild" + - chmod +x ./scripts/prefix.sh + - ./scripts/prefix.sh + +builder: + stage: build + rules: + - if: $CI_COMMIT_BRANCH == "main" + script: + - set -e + - | + if [ ! -d "../artifacts" ]; then + mkdir -p ../artifacts + fi + if [ -d "../artifacts/dist" ]; then + cp -r ../artifacts/dist frontend/dist + fi + - | + if [ -n "$(git diff --name-status HEAD~1 HEAD -- frontend)" ]; then + ./scripts/build.sh $BUILD_DIR ${VERSION} all "" + else + ./scripts/build.sh $BUILD_DIR ${VERSION} + fi + if [ -d "frontend/dist" ]; then + echo "copy frontend/dist to artifacts/dist" + rm -fr ../artifacts/dist + cp -r frontend/dist ../artifacts/dist + fi + cp $BUILD_DIR/${APP_PRE}_linux_amd64.tar.gz ${SAVE_DIR} + +deployer: + stage: deploy + rules: + - if: $CI_COMMIT_BRANCH == "main" + variables: + APIPARK_GUEST_MODE: allow + APIPARK_GUEST_ID: dklejrfbhjqwdh + script: + - cd ${SAVE_DIR};mkdir -p ${APP_PRE};tar -zxvf ${APP_PRE}_linux_amd64.tar.gz -C ${APP_PRE};cd ${APP_PRE};./install.sh ${SAVE_DIR};./run.sh restart;cd ${SAVE_DIR} && ./clean.sh ${APP_PRE} + when: on_success +success: + stage: webhook + rules: + - if: $CI_COMMIT_BRANCH == "main" + script: + - | + curl -X POST -H "Content-Type: application/json" \ + -d "{\"msg_type\":\"text\",\"content\":{\"text\":\"最近一次提交:${CI_COMMIT_TITLE}\\n提交人:${GITLAB_USER_NAME}\\n项目:${CI_PROJECT_NAME}\\n环境:${DEPLOY_DESC}\\n更新部署完成.\\n访问地址:${VIEW_ADDR}\\n工作流地址:${CI_PIPELINE_URL}\"}}" \ + https://open.feishu.cn/open-apis/bot/v2/hook/c3672932-4dfa-4989-8023-0128bae59338 + when: on_success +failure: + stage: webhook + rules: + - if: $CI_COMMIT_BRANCH == "main" + script: + - | + curl -X POST -H "Content-Type: application/json" \ + -d "{\"msg_type\":\"text\",\"content\":{\"text\":\"最近一次提交:${CI_COMMIT_TITLE}\\n提交人:${GITLAB_USER_NAME}\\n项目:${CI_PROJECT_NAME}\\n环境:${DEPLOY_DESC}\\n更新部署失败,请及时到gitlab上查看\\n工作流地址:${CI_PIPELINE_URL}\"}}" \ + https://open.feishu.cn/open-apis/bot/v2/hook/c3672932-4dfa-4989-8023-0128bae59338 + when: on_failure From ce53bb2d47fad5eee20281ddccd162c9c2c45d2a Mon Sep 17 00:00:00 2001 From: Liujian <824010343@qq.com> Date: Thu, 21 Nov 2024 17:36:13 +0800 Subject: [PATCH 02/14] Initial submission of data desensitization strategy backend --- controller/strategy/iml.go | 99 ++++++++++ controller/strategy/strategy.go | 34 ++++ module/strategy/driver/data-masking/config.go | 22 +++ module/strategy/driver/data-masking/driver.go | 81 ++++++++ module/strategy/driver/data-masking/valid.go | 30 +++ module/strategy/driver/driver.go | 11 ++ module/strategy/driver/filter.go | 132 +++++++++++++ module/strategy/driver/manager.go | 52 +++++ module/strategy/dto/enum.go | 44 +++++ module/strategy/dto/input.go | 29 +++ module/strategy/dto/output.go | 73 +++++++ module/strategy/iml.go | 167 ++++++++++++++++ module/strategy/module.go | 29 +++ module/upstream/iml.go | 3 +- plugins/core/ai.go | 2 +- plugins/core/core.go | 4 + plugins/core/strategy.go | 25 +++ service/strategy/iml.go | 182 ++++++++++++++++++ service/strategy/model.go | 77 ++++++++ service/strategy/service.go | 35 ++++ service/universally/commit/iml.go | 21 +- service/universally/commit/service.go | 6 +- service/upstream/iml.go | 4 +- service/upstream/service.go | 2 +- stores/strategy/model.go | 30 +++ stores/strategy/store.go | 20 ++ 26 files changed, 1196 insertions(+), 18 deletions(-) create mode 100644 controller/strategy/iml.go create mode 100644 controller/strategy/strategy.go create mode 100644 module/strategy/driver/data-masking/config.go create mode 100644 module/strategy/driver/data-masking/driver.go create mode 100644 module/strategy/driver/data-masking/valid.go create mode 100644 module/strategy/driver/driver.go create mode 100644 module/strategy/driver/filter.go create mode 100644 module/strategy/driver/manager.go create mode 100644 module/strategy/dto/enum.go create mode 100644 module/strategy/dto/input.go create mode 100644 module/strategy/dto/output.go create mode 100644 module/strategy/iml.go create mode 100644 module/strategy/module.go create mode 100644 plugins/core/strategy.go create mode 100644 service/strategy/iml.go create mode 100644 service/strategy/model.go create mode 100644 service/strategy/service.go create mode 100644 stores/strategy/model.go create mode 100644 stores/strategy/store.go diff --git a/controller/strategy/iml.go b/controller/strategy/iml.go new file mode 100644 index 00000000..c8ed8c59 --- /dev/null +++ b/controller/strategy/iml.go @@ -0,0 +1,99 @@ +package strategy + +import ( + "encoding/json" + "fmt" + "strconv" + + "github.com/APIParkLab/APIPark/module/service" + "github.com/APIParkLab/APIPark/module/strategy" + strategy_dto "github.com/APIParkLab/APIPark/module/strategy/dto" + "github.com/gin-gonic/gin" +) + +var _ IStrategyController = (*imlStrategyController)(nil) + +type imlStrategyController struct { + strategyModule strategy.IStrategyModule `autowired:""` + serviceModule service.IServiceModule `autowired:""` +} + +func (i *imlStrategyController) GetStrategy(ctx *gin.Context, id string) (*strategy_dto.Strategy, error) { + return i.strategyModule.Get(ctx, id) +} + +func (i *imlStrategyController) search(ctx *gin.Context, keyword string, scope strategy_dto.Scope, target string, driver string, page string, pageSize string, order string, sort string, filters string) ([]*strategy_dto.StrategyItem, int64, error) { + p, err := strconv.Atoi(page) + if err != nil { + if page != "" { + return nil, 0, fmt.Errorf("page error: %s", err) + } + p = 1 + } + ps, err := strconv.Atoi(pageSize) + if err != nil { + if pageSize != "" { + return nil, 0, fmt.Errorf("page size error: %s", err) + } + ps = 20 + } + ss := make([]string, 0) + json.Unmarshal([]byte(sort), &ss) + fs := make([]string, 0) + json.Unmarshal([]byte(filters), &fs) + list, total, err := i.strategyModule.Search(ctx, keyword, driver, scope, target, p, ps, fs, ss...) + if err != nil { + return nil, 0, err + } + + return list, total, nil +} + +func (i *imlStrategyController) GlobalStrategyList(ctx *gin.Context, keyword string, driver string, page string, pageSize string, order string, sort string, filters string) ([]*strategy_dto.StrategyItem, int64, error) { + + return i.search(ctx, keyword, strategy_dto.ToScope(strategy_dto.ScopeSystem), "", driver, page, pageSize, order, sort, filters) +} + +func (i *imlStrategyController) CreateGlobalStrategy(ctx *gin.Context, driver string, input *strategy_dto.Create) error { + input.Driver = driver + input.Scope = strategy_dto.ToScope(strategy_dto.ScopeSystem) + + return i.strategyModule.Create(ctx, input) +} + +func (i *imlStrategyController) PublishGlobalStrategy(ctx *gin.Context) error { + return i.strategyModule.Publish(ctx, strategy_dto.ScopeSystem, "") +} + +func (i *imlStrategyController) ServiceStrategyList(ctx *gin.Context, keyword string, serviceId string, driver string, page string, pageSize string, order string, sort string, filters string) ([]*strategy_dto.StrategyItem, int64, error) { + + return i.search(ctx, keyword, strategy_dto.ToScope(strategy_dto.ScopeService), serviceId, driver, page, pageSize, order, sort, filters) +} + +func (i *imlStrategyController) CreateServiceStrategy(ctx *gin.Context, serviceId string, driver string, input *strategy_dto.Create) error { + _, err := i.serviceModule.Get(ctx, serviceId) + if err != nil { + return fmt.Errorf("create service strategy error: %s", err) + } + input.Driver = driver + input.Scope = strategy_dto.ToScope(strategy_dto.ScopeService) + input.Target = serviceId + + return i.strategyModule.Create(ctx, input) +} + +func (i *imlStrategyController) EditStrategy(ctx *gin.Context, id string, input *strategy_dto.Edit) error { + return i.EditStrategy(ctx, id, input) +} + +func (i *imlStrategyController) EnableStrategy(ctx *gin.Context, id string) error { + return i.EnableStrategy(ctx, id) +} + +func (i *imlStrategyController) DisableStrategy(ctx *gin.Context, id string) error { + return i.strategyModule.Disable(ctx, id) +} + +func (i *imlStrategyController) DeleteStrategy(ctx *gin.Context, id string) error { + return i.strategyModule.Delete(ctx, id) +} diff --git a/controller/strategy/strategy.go b/controller/strategy/strategy.go new file mode 100644 index 00000000..8dbb04fb --- /dev/null +++ b/controller/strategy/strategy.go @@ -0,0 +1,34 @@ +package strategy + +import ( + "reflect" + + strategy_dto "github.com/APIParkLab/APIPark/module/strategy/dto" + "github.com/eolinker/go-common/autowire" + "github.com/gin-gonic/gin" +) + +type IStrategyController interface { + GlobalStrategyList(ctx *gin.Context, keyword string, driver string, page string, pageSize string, order string, sort string, filters string) ([]*strategy_dto.StrategyItem, int64, error) + CreateGlobalStrategy(ctx *gin.Context, driver string, input *strategy_dto.Create) error + PublishGlobalStrategy(ctx *gin.Context) error + + ServiceStrategyList(ctx *gin.Context, keyword string, serviceId string, driver string, page string, pageSize string, order string, sort string, filters string) ([]*strategy_dto.StrategyItem, int64, error) + CreateServiceStrategy(ctx *gin.Context, serviceId string, driver string, input *strategy_dto.Create) error + + EditStrategy(ctx *gin.Context, id string, input *strategy_dto.Edit) error + GetStrategy(ctx *gin.Context, id string) (*strategy_dto.Strategy, error) + EnableStrategy(ctx *gin.Context, id string) error + DisableStrategy(ctx *gin.Context, id string) error + + DeleteStrategy(ctx *gin.Context, id string) error +} + +type IStrategyCommonController interface { +} + +func init() { + autowire.Auto[IStrategyController](func() reflect.Value { + return reflect.ValueOf(&imlStrategyController{}) + }) +} diff --git a/module/strategy/driver/data-masking/config.go b/module/strategy/driver/data-masking/config.go new file mode 100644 index 00000000..7aa0df12 --- /dev/null +++ b/module/strategy/driver/data-masking/config.go @@ -0,0 +1,22 @@ +package data_masking + +type Config struct { + Rules []*Rule `json:"rules"` +} + +type Rule struct { + Match *BasicItem `json:"match"` + Mask *Mask `json:"mask"` +} + +type BasicItem struct { + Type string `json:"type"` + Value string `json:"value"` +} + +type Mask struct { + Type string `json:"type"` + Begin int `json:"begin"` + Length int `json:"length"` + Replace *BasicItem `json:"replace"` +} diff --git a/module/strategy/driver/data-masking/driver.go b/module/strategy/driver/data-masking/driver.go new file mode 100644 index 00000000..f3608d82 --- /dev/null +++ b/module/strategy/driver/data-masking/driver.go @@ -0,0 +1,81 @@ +package data_masking + +import ( + "encoding/json" + "errors" + "fmt" + + strategy_driver "github.com/APIParkLab/APIPark/module/strategy/driver" + + strategy_dto "github.com/APIParkLab/APIPark/module/strategy/dto" +) + +func init() { + strategy_driver.Register(&strategyDriver{confName: "data_mask"}) +} + +type strategyDriver struct { + confName string +} + +func (d *strategyDriver) Driver() string { + return "data-masking" +} + +func (d *strategyDriver) ToApinto(s strategy_dto.Strategy) interface{} { + filters := make(map[string][]string) + + for _, f := range s.Filters { + filters[f.Name] = f.Values + } + + return map[string]interface{}{ + "name": s.Filters, + "description": s.Desc, + "priority": s.Priority, + "filters": filters, + d.confName: s.Config, + } +} + +func (d *strategyDriver) Check(config interface{}) error { + if config == nil { + return nil + } + data, err := json.Marshal(config) + if err != nil { + return err + } + var cfg Config + err = json.Unmarshal(data, &cfg) + if err != nil { + return err + } + for _, r := range cfg.Rules { + if r.Match == nil { + return errors.New("match can't be null. ") + } + if r.Mask == nil { + return errors.New("mask can't be null. ") + } + + if _, ok := validMatchTypes[r.Match.Type]; !ok { + return fmt.Errorf("match type %s is illegal. ", r.Match.Type) + } + + if r.Match.Type == "inner" { + if _, ok := validMatchInnerValues[r.Match.Value]; !ok { + return fmt.Errorf("match value %s is illegal. ", r.Match.Value) + } + } + if _, ok := validMaskTypes[r.Mask.Type]; !ok { + return fmt.Errorf("mask type %s is illegal. ", r.Mask.Type) + } + if r.Mask.Replace != nil { + if _, ok := validReplaceTypes[r.Mask.Replace.Type]; !ok { + return fmt.Errorf("replace type %s is illegal. ", r.Mask.Replace.Type) + } + } + } + return nil +} diff --git a/module/strategy/driver/data-masking/valid.go b/module/strategy/driver/data-masking/valid.go new file mode 100644 index 00000000..1f018c93 --- /dev/null +++ b/module/strategy/driver/data-masking/valid.go @@ -0,0 +1,30 @@ +package data_masking + +var validMatchInnerValues = map[string]struct{}{ + "name": {}, + "phone": {}, + "email": {}, + "id-card": {}, + "bank-card": {}, + "date": {}, + "amount": {}, +} +var validMatchTypes = map[string]struct{}{ + "inner": {}, + "keyword": {}, + "regex": {}, + "json_path": {}, +} + +var validMaskTypes = map[string]struct{}{ + "partial-display": {}, + "partial-masking": {}, + "truncation": {}, + "replacement": {}, + "shuffling": {}, +} + +var validReplaceTypes = map[string]struct{}{ + "random": {}, + "custom": {}, +} diff --git a/module/strategy/driver/driver.go b/module/strategy/driver/driver.go new file mode 100644 index 00000000..1bb5e4a2 --- /dev/null +++ b/module/strategy/driver/driver.go @@ -0,0 +1,11 @@ +package strategy_driver + +import ( + strategy_dto "github.com/APIParkLab/APIPark/module/strategy/dto" +) + +type IStrategyDriver interface { + Driver() string + ToApinto(strategy strategy_dto.Strategy) interface{} + Check(config interface{}) error +} diff --git a/module/strategy/driver/filter.go b/module/strategy/driver/filter.go new file mode 100644 index 00000000..0fdd9b6a --- /dev/null +++ b/module/strategy/driver/filter.go @@ -0,0 +1,132 @@ +package strategy_driver + +import ( + "fmt" + "regexp" + + strategy_dto "github.com/APIParkLab/APIPark/module/strategy/dto" +) + +type FilterOptionsItem struct { + Name string + Title string + Type string + Pattern *regexp.Regexp + Options []string +} + +const ( + FilterMethod = "method" + FilterPath = "path" + FilterIP = "ip" + FilterApplication = "application" + FilterApi = "api" + FilterService = "service" + FilterAppKey = "appkey" + + FilterTypeRemote = "remote" + FilterTypePattern = "pattern" + FilterTypeStatic = "static" + + FilterValuesALL = "ALL" +) + +const ( + ApiPathRegexp = `^\*?[\w-/]+\*?$` + CIDRIpv4Exp = `^(?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([1-9]|[1-2]\d|3[0-2]))?$` +) + +const ( + HttpALL = "ALL" + HttpGET = "GET" + HttpPOST = "POST" + HttpPUT = "PUT" + HttpDELETE = "DELETE" + HttpPATCH = "PATCH" + HttpHEADER = "HEADER" + HttpOPTIONS = "OPTIONS" +) + +var ( + staticOptions = []*FilterOptionsItem{ + { + Name: FilterMethod, + Title: "API请求方式", + Type: FilterTypeStatic, + Options: []string{HttpALL, HttpGET, HttpPOST, HttpPUT, HttpDELETE, HttpPATCH, HttpHEADER, HttpOPTIONS}, + }, { + Name: FilterPath, + Title: "API路径", + Type: FilterTypePattern, + Pattern: regexp.MustCompile(ApiPathRegexp), + }, { + Name: FilterIP, + Title: "IP", + Type: FilterTypePattern, + Pattern: regexp.MustCompile(CIDRIpv4Exp), + }, + } + globalFilters = map[string]struct{}{} + serviceFilters = map[string]struct{}{} +) + +func init() { + for _, option := range staticOptions { + globalFilters[option.Name] = struct{}{} + } + for _, option := range staticOptions { + serviceFilters[option.Name] = struct{}{} + } +} + +func CheckFilters(name string, scope strategy_dto.Scope, filters []*strategy_dto.Filter) error { + var fs map[string]struct{} + switch scope.String() { + case strategy_dto.ScopeSystem: + fs = globalFilters + case strategy_dto.ScopeService: + fs = serviceFilters + default: + return fmt.Errorf("unknown scope %s", scope) + } + filterNameSet := make(map[string]struct{}) + for _, filter := range filters { + _, ok := fs[filter.Name] + if !ok { + return fmt.Errorf("%s filter %s not found", name, filter.Name) + } + if len(filter.Values) == 0 { + return fmt.Errorf("%s.Options can't be null. filter.Name:%s ", name, filter.Name) + } + + if _, has := filterNameSet[filter.Name]; has { + return fmt.Errorf("%s.Name %s is reduplicative. ", name, filter.Name) + } + filterNameSet[filter.Name] = struct{}{} + } + + return nil +} + +type OptionTitle struct { + Field string `json:"field"` + Title string `json:"title" aoi18n:""` +} + +type FilterOptionConfig struct { + Title string + Titles []OptionTitle + Key string +} + +type IFilterOptionHandler interface { + Name() string + Config() FilterOptionConfig + GetOptions(keyword, conditions map[string]interface{}, pageNum, pageSize int) ([]any, int) + Labels(values ...string) []string + Label(value string) string +} + +type IFilterFactory interface { + GetHandler(name string) IFilterOptionHandler +} diff --git a/module/strategy/driver/manager.go b/module/strategy/driver/manager.go new file mode 100644 index 00000000..80e04187 --- /dev/null +++ b/module/strategy/driver/manager.go @@ -0,0 +1,52 @@ +package strategy_driver + +import ( + "fmt" + + "github.com/eolinker/eosc" +) + +var manager = newManager() + +func newManager() *Manager { + return &Manager{ + drivers: eosc.BuildUntyped[string, IStrategyDriver](), + } +} + +type Manager struct { + drivers eosc.Untyped[string, IStrategyDriver] +} + +func (m *Manager) AddDriver(driver IStrategyDriver) { + m.drivers.Set(driver.Driver(), driver) +} + +func (m *Manager) GetDriver(driver string) (IStrategyDriver, bool) { + return m.drivers.Get(driver) +} + +func (m *Manager) GetDrivers() []string { + return m.drivers.Keys() +} + +func (m *Manager) Delete(name string) { + m.drivers.Del(name) +} + +func GetDriver(name string) (IStrategyDriver, bool) { + return manager.GetDriver(name) +} + +func Register(driver IStrategyDriver) { + manager.AddDriver(driver) +} + +func CheckConfig(name string, config interface{}) error { + driver, has := manager.GetDriver(name) + if !has { + return fmt.Errorf("driver %s not found", name) + } + + return driver.Check(config) +} diff --git a/module/strategy/dto/enum.go b/module/strategy/dto/enum.go new file mode 100644 index 00000000..9ba86462 --- /dev/null +++ b/module/strategy/dto/enum.go @@ -0,0 +1,44 @@ +package strategy_dto + +const ( + ScopeSystem = "system" + ScopeTeam = "team" + ScopeService = "service" + + PublishStatusOnline = "online" + PublishStatusOffline = "offline" + PublishStatusUpdate = "update" + PublishStatusDelete = "delete" +) + +type Scope int + +func (s Scope) String() string { + switch s { + case 0: + return ScopeSystem + case 1: + return ScopeTeam + case 2: + return ScopeService + default: + return ScopeSystem + } +} + +func (s Scope) Int() int { + return int(s) +} + +func ToScope(s string) Scope { + switch s { + case ScopeSystem: + return 0 + case ScopeTeam: + return 1 + case ScopeService: + return 2 + default: + return 0 + } +} diff --git a/module/strategy/dto/input.go b/module/strategy/dto/input.go new file mode 100644 index 00000000..45bbd09e --- /dev/null +++ b/module/strategy/dto/input.go @@ -0,0 +1,29 @@ +package strategy_dto + +type Create struct { + Scope Scope `json:"-"` + Target string `json:"-"` + Driver string `json:"-"` + ID string `json:"id"` + Name string `json:"name"` + Priority int `json:"priority"` + Desc string `json:"desc"` + Filters []*Filter `json:"filters"` + Config interface{} `json:"config"` +} + +type Edit struct { + Name *string `json:"name"` + Priority *int `json:"priority"` + Desc *string `json:"desc"` + Filters *[]*Filter `json:"filters"` + Config *interface{} `json:"config"` +} + +type Filter struct { + Name string `json:"name"` + Values []string `json:"values"` + Type string `json:"type"` + Label string `json:"label"` + Title string `json:"title"` +} diff --git a/module/strategy/dto/output.go b/module/strategy/dto/output.go new file mode 100644 index 00000000..5c4727de --- /dev/null +++ b/module/strategy/dto/output.go @@ -0,0 +1,73 @@ +package strategy_dto + +import ( + "encoding/json" + + "github.com/APIParkLab/APIPark/service/strategy" + "github.com/eolinker/go-common/auto" +) + +func ToStrategyItem(s *strategy.Strategy, publishVersion string) *StrategyItem { + publishStatus := PublishStatusOffline + if publishVersion != "" { + if s.IsDelete { + publishStatus = PublishStatusDelete + } else { + version := s.UpdateAt.Format("20060102150405") + if version != publishVersion { + publishStatus = PublishStatusUpdate + } else { + publishStatus = PublishStatusOnline + } + } + } + return &StrategyItem{ + Id: s.Id, + Name: s.Name, + Priority: 0, + Desc: s.Desc, + Filters: "", + Updater: auto.UUID(s.Updater), + UpdateTime: auto.TimeLabel(s.UpdateAt), + ProcessedTotal: 0, + PublishStatus: publishStatus, + IsStop: s.IsStop, + } +} + +func ToStrategy(s *strategy.Strategy) *Strategy { + filters := make([]*Filter, 0) + json.Unmarshal([]byte(s.Filters), &filters) + var cfg interface{} + json.Unmarshal([]byte(s.Config), &cfg) + return &Strategy{ + Id: s.Id, + Name: s.Name, + Priority: s.Priority, + Desc: s.Desc, + Filters: filters, + Config: cfg, + } +} + +type Strategy struct { + Id string `json:"id"` + Name string `json:"name"` + Priority int `json:"priority"` + Desc string `json:"desc"` + Filters []*Filter `json:"filters"` + Config interface{} `json:"config"` +} + +type StrategyItem struct { + Id string `json:"id"` + Name string `json:"name"` + Priority int `json:"priority"` + Desc string `json:"desc"` + Filters string `json:"filters"` + Updater auto.Label `json:"updater" aolabel:"user"` + UpdateTime auto.TimeLabel `json:"update_time"` + ProcessedTotal int `json:"processed_total"` + PublishStatus string `json:"publish_status"` + IsStop bool `json:"is_stop"` +} diff --git a/module/strategy/iml.go b/module/strategy/iml.go new file mode 100644 index 00000000..c6e7c756 --- /dev/null +++ b/module/strategy/iml.go @@ -0,0 +1,167 @@ +package strategy + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/eolinker/go-common/store" + + "github.com/APIParkLab/APIPark/service/universally/commit" + + "github.com/eolinker/go-common/utils" + + "github.com/google/uuid" + + strategy_driver "github.com/APIParkLab/APIPark/module/strategy/driver" + + strategy_dto "github.com/APIParkLab/APIPark/module/strategy/dto" + + "github.com/APIParkLab/APIPark/service/strategy" +) + +var _ IStrategyModule = (*imlStrategyModule)(nil) + +type imlStrategyModule struct { + strategyService strategy.IStrategyService `autowired:""` + transaction store.ITransaction `autowired:""` +} + +func (i *imlStrategyModule) Search(ctx context.Context, keyword string, driver string, scope strategy_dto.Scope, target string, page int, pageSize int, filters []string, order ...string) ([]*strategy_dto.StrategyItem, int64, error) { + list, total, err := i.strategyService.Search(ctx, keyword, driver, scope.Int(), target, page, pageSize, filters, order...) + if err != nil { + return nil, 0, err + } + strategyIds := utils.SliceToSlice(list, func(l *strategy.Strategy) string { return l.Id }) + commits, err := i.strategyService.ListLatestStrategyCommit(ctx, scope.String(), target, strategyIds...) + if err != nil { + return nil, 0, err + } + commitMap := utils.SliceToMapO(commits, func(c *commit.Commit[strategy.StrategyCommit]) (string, string) { return c.Key, c.Data.Version }) + items := make([]*strategy_dto.StrategyItem, 0, len(list)) + for _, l := range list { + item := strategy_dto.ToStrategyItem(l, commitMap[l.Id]) + items = append(items, item) + } + return items, total, nil +} + +func (i *imlStrategyModule) Get(ctx context.Context, id string) (*strategy_dto.Strategy, error) { + info, err := i.strategyService.Get(ctx, id) + if err != nil { + return nil, err + } + return strategy_dto.ToStrategy(info), nil +} + +func (i *imlStrategyModule) Create(ctx context.Context, input *strategy_dto.Create) error { + if input.Name == "" { + return fmt.Errorf("name required") + } + if input.ID == "" { + input.ID = uuid.NewString() + } + + if input.Priority < 1 { + input.Priority = 1000 + } + err := strategy_driver.CheckFilters(input.Driver, input.Scope, input.Filters) + if err != nil { + return err + } + + err = strategy_driver.CheckConfig(input.Driver, input.Config) + if err != nil { + return err + } + filters, _ := json.Marshal(input.Filters) + cfg, _ := json.Marshal(input.Config) + return i.strategyService.Create(ctx, &strategy.Create{ + Id: input.ID, + Name: input.Name, + Priority: input.Priority, + Desc: input.Desc, + Filters: string(filters), + Config: string(cfg), + Scope: input.Scope.Int(), + Target: input.Target, + Driver: input.Driver, + }) +} + +func (i *imlStrategyModule) Edit(ctx context.Context, id string, input *strategy_dto.Edit) error { + if input.Name != nil && *input.Name == "" { + return fmt.Errorf("name required") + } + info, err := i.strategyService.Get(ctx, id) + if err != nil { + return err + } + if input.Priority != nil && *input.Priority < 1 { + *input.Priority = 1000 + } + filters := info.Filters + if input.Filters != nil { + err = strategy_driver.CheckFilters(info.Driver, strategy_dto.Scope(info.Scope), *input.Filters) + if err != nil { + return err + } + data, _ := json.Marshal(input.Filters) + filters = string(data) + } + cfg := info.Config + if input.Config != nil { + err = strategy_driver.CheckConfig(info.Driver, input.Config) + if err != nil { + return err + } + data, _ := json.Marshal(input.Config) + cfg = string(data) + } + + return i.strategyService.Save(ctx, id, &strategy.Edit{ + Name: input.Name, + Priority: input.Priority, + Desc: input.Desc, + Filters: &filters, + Config: &cfg, + }) +} + +func (i *imlStrategyModule) Enable(ctx context.Context, id string) error { + stop := false + return i.strategyService.Save(ctx, id, &strategy.Edit{IsStop: &stop}) +} + +func (i *imlStrategyModule) Disable(ctx context.Context, id string) error { + stop := true + return i.strategyService.Save(ctx, id, &strategy.Edit{IsStop: &stop}) +} + +func (i *imlStrategyModule) Publish(ctx context.Context, scope string, target string) error { + list, err := i.strategyService.AllByScope(ctx, strategy_dto.ToScope(scope).Int(), target) + if err != nil { + return err + } + return i.transaction.Transaction(ctx, func(txCtx context.Context) error { + for _, l := range list { + if l.IsDelete { + err = i.strategyService.Delete(ctx, l.Id) + if err != nil { + return err + } + } + + // TODO:同步到网关 + err = i.strategyService.CommitStrategy(txCtx, scope, target, l.Id, l) + if err != nil { + return err + } + } + return nil + }) +} + +func (i *imlStrategyModule) Delete(ctx context.Context, id string) error { + return i.strategyService.SortDelete(ctx, id) +} diff --git a/module/strategy/module.go b/module/strategy/module.go new file mode 100644 index 00000000..fde3e317 --- /dev/null +++ b/module/strategy/module.go @@ -0,0 +1,29 @@ +package strategy + +import ( + "context" + "reflect" + + "github.com/eolinker/go-common/autowire" + + _ "github.com/APIParkLab/APIPark/module/strategy/driver/data-masking" + strategy_dto "github.com/APIParkLab/APIPark/module/strategy/dto" +) + +type IStrategyModule interface { + Search(ctx context.Context, keyword string, driver string, scope strategy_dto.Scope, target string, page int, pageSize int, filters []string, order ...string) ([]*strategy_dto.StrategyItem, int64, error) + Get(ctx context.Context, id string) (*strategy_dto.Strategy, error) + Create(ctx context.Context, i *strategy_dto.Create) error + Edit(ctx context.Context, id string, i *strategy_dto.Edit) error + Enable(ctx context.Context, id string) error + Disable(ctx context.Context, id string) error + Publish(ctx context.Context, scope string, target string) error + Delete(ctx context.Context, id string) error +} + +func init() { + strategyModule := new(imlStrategyModule) + autowire.Auto[IStrategyModule](func() reflect.Value { + return reflect.ValueOf(strategyModule) + }) +} diff --git a/module/upstream/iml.go b/module/upstream/iml.go index 67a9a03b..ec00d397 100644 --- a/module/upstream/iml.go +++ b/module/upstream/iml.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "github.com/APIParkLab/APIPark/service/universally/commit" "github.com/eolinker/go-common/utils" @@ -34,7 +35,7 @@ type imlUpstreamModule struct { } func (i *imlUpstreamModule) ExportAll(ctx context.Context) ([]*upstream_dto.ExportUpstream, error) { - latestCommits, err := i.upstreamService.ListLatestCommit(ctx) + latestCommits, err := i.upstreamService.ListLatestCommit(ctx, cluster.DefaultClusterID) if err != nil { return nil, err } diff --git a/plugins/core/ai.go b/plugins/core/ai.go index 7cae2840..c73e8173 100644 --- a/plugins/core/ai.go +++ b/plugins/core/ai.go @@ -14,7 +14,7 @@ func (p *plugin) aiAPIs() []pm3.Api { pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/simple/ai/providers", []string{"context"}, []string{"providers"}, p.aiProviderController.SimpleProviders), pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/ai/provider/config", []string{"context", "query:provider"}, []string{"provider"}, p.aiProviderController.Provider, access.SystemSettingsAiProviderView), pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/ai/provider/llms", []string{"context", "query:provider"}, []string{"llms", "provider"}, p.aiProviderController.LLMs), - //pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/ai/provider/enable", []string{"context", "query:provider"}, nil, p.aiProviderController.Enable), + //pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/ai/provider/enable", []string{"context", "query:provider"}, nil, p.aiProviderController.isStop), //pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/ai/provider/disable", []string{"context", "query:provider"}, nil, p.aiProviderController.Disable), pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/ai/provider/config", []string{"context", "query:provider", "body"}, nil, p.aiProviderController.UpdateProviderConfig, access.SystemSettingsAiProviderManager), //pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/ai/provider/default-llm", []string{"context", "query:provider", "body"}, nil, p.aiProviderController.UpdateProviderDefaultLLM), diff --git a/plugins/core/core.go b/plugins/core/core.go index 8d7168ca..1049006d 100644 --- a/plugins/core/core.go +++ b/plugins/core/core.go @@ -3,6 +3,8 @@ package core import ( "net/http" + "github.com/APIParkLab/APIPark/controller/strategy" + "github.com/APIParkLab/APIPark/controller/ai" ai_api "github.com/APIParkLab/APIPark/controller/ai-api" "github.com/APIParkLab/APIPark/controller/monitor" @@ -72,6 +74,7 @@ type plugin struct { aiAPIController ai_api.IAPIController `autowired:""` apiDocController router.IAPIDocController `autowired:""` subscribeController subscribe.ISubscribeController `autowired:""` + strategyController strategy.IStrategyController `autowired:""` appAuthorizationController application_authorization.IAuthorizationController `autowired:""` releaseController release.IReleaseController `autowired:""` roleController role.IRoleController `autowired:""` @@ -105,6 +108,7 @@ func (p *plugin) OnComplete() { p.apis = append(p.apis, p.commonApis()...) p.apis = append(p.apis, p.systemApis()...) p.apis = append(p.apis, p.aiAPIs()...) + p.apis = append(p.apis, p.strategyApis()...) } func (p *plugin) Name() string { diff --git a/plugins/core/strategy.go b/plugins/core/strategy.go new file mode 100644 index 00000000..aa811063 --- /dev/null +++ b/plugins/core/strategy.go @@ -0,0 +1,25 @@ +package core + +import ( + "net/http" + + "github.com/eolinker/go-common/pm3" +) + +func (p *plugin) strategyApis() []pm3.Api { + return []pm3.Api{ + pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/strategy/global/:driver/list", []string{"context", "query:keyword", "rest:driver", "query:page", "query:page_size", "query:order", "query:sort", "query:filters"}, []string{"list", "total"}, p.strategyController.GlobalStrategyList), + pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/strategy/global/:driver", []string{"context", "query:strategy"}, []string{"strategy"}, p.strategyController.GetStrategy), + pm3.CreateApiWidthDoc(http.MethodPost, "/api/v1/strategy/global/:driver", []string{"context", "rest:driver", "body"}, nil, p.strategyController.CreateGlobalStrategy), + pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/strategy/global/:driver", []string{"context", "query:strategy", "body"}, nil, p.strategyController.EditStrategy), + pm3.CreateApiWidthDoc(http.MethodDelete, "/api/v1/strategy/global/:driver", []string{"context", "query:strategy"}, nil, p.strategyController.DeleteStrategy), + pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/strategy/global/:driver/enable", []string{"context", "query:strategy"}, nil, p.strategyController.EnableStrategy), + pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/strategy/global/:driver/disable", []string{"context", "query:strategy"}, nil, p.strategyController.DisableStrategy), + + pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/strategy/service/:driver/list", []string{"context", "query:keyword", "query:service", "rest:driver", "query:page", "query:page_size", "query:order", "query:sort", "query:filters"}, []string{"list", "total"}, p.strategyController.ServiceStrategyList), + pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/strategy/service/:driver", []string{"context", "query:strategy"}, []string{"strategy"}, p.strategyController.GetStrategy), + pm3.CreateApiWidthDoc(http.MethodPost, "/api/v1/strategy/service/:driver", []string{"context", "query:service", "rest:driver", "body"}, nil, p.strategyController.CreateServiceStrategy), + pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/strategy/service/:driver/enable", []string{"context", "query:strategy"}, nil, p.strategyController.EnableStrategy), + pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/strategy/service/:driver/disable", []string{"context", "query:strategy"}, nil, p.strategyController.DisableStrategy), + } +} diff --git a/service/strategy/iml.go b/service/strategy/iml.go new file mode 100644 index 00000000..5831d7a9 --- /dev/null +++ b/service/strategy/iml.go @@ -0,0 +1,182 @@ +package strategy + +import ( + "context" + "fmt" + "time" + + "github.com/APIParkLab/APIPark/service/universally/commit" + + "github.com/eolinker/go-common/utils" + + "github.com/APIParkLab/APIPark/service/universally" + "github.com/APIParkLab/APIPark/stores/strategy" +) + +var _ IStrategyService = (*imlStrategyService)(nil) + +type imlStrategyService struct { + store strategy.IStrategyStore `autowired:""` + commitService commit.ICommitService[StrategyCommit] `autowired:""` + universally.IServiceCreate[Create] + universally.IServiceEdit[Edit] +} + +func (i *imlStrategyService) AllByScope(ctx context.Context, scope int, target string) ([]*Strategy, error) { + w := make(map[string]interface{}) + w["scope"] = scope + if target != "" { + w["target"] = target + } + list, err := i.store.List(ctx, w) + if err != nil { + return nil, err + } + return utils.SliceToSlice(list, FromEntity), nil +} + +func (i *imlStrategyService) CommitStrategy(ctx context.Context, scope string, target string, strategyId string, data *Strategy) error { + key := scope + if target != "" { + key = fmt.Sprintf("%s-%s", scope, target) + } + + return i.commitService.Save(ctx, strategyId, key, &StrategyCommit{ + Id: data.Id, + Name: data.Name, + Priority: data.Priority, + Filters: data.Filters, + Config: data.Config, + Driver: data.Driver, + IsStop: data.IsStop, + Version: data.UpdateAt.Format("20060102150405"), + }) +} + +func (i *imlStrategyService) GetStrategyCommit(ctx context.Context, commitId string) (*commit.Commit[StrategyCommit], error) { + return i.commitService.Get(ctx, commitId) +} + +func (i *imlStrategyService) LatestStrategyCommit(ctx context.Context, scope string, target string, strategyId string) (*commit.Commit[StrategyCommit], error) { + key := scope + if target != "" { + key = fmt.Sprintf("%s-%s", scope, target) + } + return i.commitService.Latest(ctx, strategyId, key) +} + +func (i *imlStrategyService) ListLatestStrategyCommit(ctx context.Context, scope string, target string, strategyIds ...string) ([]*commit.Commit[StrategyCommit], error) { + key := scope + if target != "" { + key = fmt.Sprintf("%s-%s", scope, target) + } + return i.commitService.ListLatest(ctx, key, strategyIds...) +} + +func (i *imlStrategyService) ListStrategyCommit(ctx context.Context, commitIds ...string) ([]*commit.Commit[StrategyCommit], error) { + if len(commitIds) < 1 { + return nil, fmt.Errorf("commit ids is empty") + } + + return i.commitService.List(ctx, commitIds...) +} + +func (i *imlStrategyService) Search(ctx context.Context, keyword string, driver string, scope int, target string, page int, pageSize int, filters []string, order ...string) ([]*Strategy, int64, error) { + w := map[string]interface{}{ + "scope": scope, + "driver": driver, + } + if target != "" { + w["target"] = target + } + for _, f := range filters { + switch f { + case "enable": + w["enable"] = true + case "disable": + w["enable"] = false + } + } + if len(order) < 1 { + order = []string{"update_at desc"} + } + list, total, err := i.store.SearchByPage(ctx, keyword, w, page, pageSize, order...) + if err != nil { + return nil, 0, err + } + return utils.SliceToSlice(list, FromEntity), total, nil +} + +func (i *imlStrategyService) Get(ctx context.Context, id string) (*Strategy, error) { + info, err := i.store.GetByUUID(ctx, id) + if err != nil { + return nil, err + } + return FromEntity(info), nil +} + +func (i *imlStrategyService) SortDelete(ctx context.Context, id string) error { + return i.store.SoftDelete(ctx, map[string]interface{}{"uuid": id}) +} + +func (i *imlStrategyService) Delete(ctx context.Context, id ...string) error { + if len(id) == 0 { + return nil + } + _, err := i.store.DeleteWhere(ctx, map[string]interface{}{"uuid": id}) + if err != nil { + return err + } + return nil +} + +func (i *imlStrategyService) OnComplete() { + + i.IServiceCreate = universally.NewCreator[Create, strategy.Strategy](i.store, "strategy", createEntityHandler, uniquestHandler, labelHandler) + + i.IServiceEdit = universally.NewEdit[Edit, strategy.Strategy](i.store, updateHandler, labelHandler) +} + +func labelHandler(e *strategy.Strategy) []string { + return []string{e.Name, e.UUID, e.Desc} +} +func uniquestHandler(i *Create) []map[string]interface{} { + return []map[string]interface{}{{"uuid": i.Id}} +} +func createEntityHandler(i *Create) *strategy.Strategy { + now := time.Now() + return &strategy.Strategy{ + UUID: i.Id, + Name: i.Name, + Priority: i.Priority, + Desc: i.Desc, + Filters: i.Filters, + Config: i.Config, + Scope: i.Scope, + Target: i.Target, + CreateAt: now, + UpdateAt: now, + IsStop: true, + } +} +func updateHandler(e *strategy.Strategy, i *Edit) { + if i.Name != nil { + e.Name = *i.Name + } + if i.Priority != nil { + e.Priority = *i.Priority + } + if i.Desc != nil { + e.Desc = *i.Desc + } + if i.Filters != nil { + e.Filters = *i.Filters + } + if i.Config != nil { + e.Config = *i.Config + } + if i.IsStop != nil { + e.IsStop = *i.IsStop + } + +} diff --git a/service/strategy/model.go b/service/strategy/model.go new file mode 100644 index 00000000..b6217850 --- /dev/null +++ b/service/strategy/model.go @@ -0,0 +1,77 @@ +package strategy + +import ( + "time" + + "github.com/APIParkLab/APIPark/stores/strategy" +) + +type Strategy struct { + Id string + Name string + Priority int + Desc string + Filters string + Config string + Driver string + Scope int + Target string + Creator string + Updater string + CreateAt time.Time + UpdateAt time.Time + IsStop bool + IsDelete bool +} + +func FromEntity(e *strategy.Strategy) *Strategy { + return &Strategy{ + Id: e.UUID, + Name: e.Name, + Priority: e.Priority, + Driver: e.Driver, + Desc: e.Desc, + Filters: e.Filters, + Config: e.Config, + Scope: e.Scope, + Target: e.Target, + Creator: e.Creator, + Updater: e.Updater, + CreateAt: e.CreateAt, + UpdateAt: e.UpdateAt, + IsStop: e.IsStop, + IsDelete: e.IsDelete, + } +} + +type Create struct { + Id string + Name string + Priority int + Desc string + Filters string + Config string + Scope int + Target string + Driver string +} + +type Edit struct { + Name *string + Priority *int + Desc *string + Filters *string + Config *string + IsStop *bool +} + +type StrategyCommit struct { + Id string + Name string + Priority int + Filters string + Config string + Driver string + IsStop bool + Version string +} diff --git a/service/strategy/service.go b/service/strategy/service.go new file mode 100644 index 00000000..557aeb51 --- /dev/null +++ b/service/strategy/service.go @@ -0,0 +1,35 @@ +package strategy + +import ( + "context" + "reflect" + + "github.com/APIParkLab/APIPark/service/universally/commit" + + "github.com/eolinker/go-common/autowire" + + "github.com/APIParkLab/APIPark/service/universally" +) + +type IStrategyService interface { + universally.IServiceCreate[Create] + universally.IServiceEdit[Edit] + AllByScope(ctx context.Context, scope int, target string) ([]*Strategy, error) + Search(ctx context.Context, keyword string, driver string, scope int, target string, page int, pageSize int, filters []string, order ...string) ([]*Strategy, int64, error) + Get(ctx context.Context, id string) (*Strategy, error) + SortDelete(ctx context.Context, id string) error + Delete(ctx context.Context, id ...string) error + + CommitStrategy(ctx context.Context, scope string, target string, strategyId string, data *Strategy) error + GetStrategyCommit(ctx context.Context, commitId string) (*commit.Commit[StrategyCommit], error) + LatestStrategyCommit(ctx context.Context, scope string, target string, strategyId string) (*commit.Commit[StrategyCommit], error) + ListLatestStrategyCommit(ctx context.Context, scope string, target string, strategyIds ...string) ([]*commit.Commit[StrategyCommit], error) + ListStrategyCommit(ctx context.Context, commitIds ...string) ([]*commit.Commit[StrategyCommit], error) +} + +func init() { + autowire.Auto[IStrategyService](func() reflect.Value { + return reflect.ValueOf(new(imlStrategyService)) + }) + commit.InitCommitService[StrategyCommit]("strategy") +} diff --git a/service/universally/commit/iml.go b/service/universally/commit/iml.go index fa7c6bcf..a0a542ef 100644 --- a/service/universally/commit/iml.go +++ b/service/universally/commit/iml.go @@ -2,9 +2,10 @@ package commit import ( "context" + "github.com/eolinker/go-common/utils" "gorm.io/gorm" - + "github.com/APIParkLab/APIPark/stores/universally/commit" ) @@ -18,7 +19,7 @@ type imlCommitWithKeyService[T any] struct { } func (i *imlCommitWithKeyService[T]) List(ctx context.Context, uuids ...string) ([]*Commit[T], error) { - + list, err := i.store.List(ctx, uuids...) if err != nil { return nil, err @@ -31,7 +32,7 @@ func (i *imlCommitWithKeyService[T]) ListLatest(ctx context.Context, target ...s if err != nil { return nil, err } - + return utils.SliceToSlice(list, newCommit[T]), nil } @@ -40,7 +41,7 @@ func (i *imlCommitWithKeyService[T]) Get(ctx context.Context, uuid string) (*Com if err != nil { return nil, err } - + return newCommit(r), nil } @@ -52,7 +53,7 @@ func (i *imlCommitWithKeyService[T]) Latest(ctx context.Context, target string) if len(list) == 0 { return nil, gorm.ErrRecordNotFound } - + result := list[0] return result, nil } @@ -70,13 +71,13 @@ func (i *imlCommitService[T]) List(ctx context.Context, uuids ...string) ([]*Com if err != nil { return nil, err } - + return utils.SliceToSlice(list, newCommit[T]), nil - + } -func (i *imlCommitService[T]) ListLatest(ctx context.Context, target ...string) ([]*Commit[T], error) { - list, err := i.store.Latest(ctx, "", target...) +func (i *imlCommitService[T]) ListLatest(ctx context.Context, key string, target ...string) ([]*Commit[T], error) { + list, err := i.store.Latest(ctx, key, target...) if err != nil { return nil, err } @@ -88,7 +89,7 @@ func (i *imlCommitService[T]) Get(ctx context.Context, uuid string) (*Commit[T], if err != nil { return nil, err } - + return newCommit(r), nil } diff --git a/service/universally/commit/service.go b/service/universally/commit/service.go index 5c5cbda4..215dca79 100644 --- a/service/universally/commit/service.go +++ b/service/universally/commit/service.go @@ -3,7 +3,7 @@ package commit import ( "context" "reflect" - + "github.com/APIParkLab/APIPark/stores/universally/commit" "github.com/eolinker/go-common/autowire" ) @@ -18,7 +18,7 @@ type ICommitWithKeyService[T any] interface { func InitCommitWithKeyService[T any](name string, key string) { autowire.Auto[commit.ICommitWKStore[T]](func() reflect.Value { - + return reflect.ValueOf(commit.NewCommitWithKey[T](name, key)) }) autowire.Auto[ICommitWithKeyService[T]](func() reflect.Value { @@ -28,7 +28,7 @@ func InitCommitWithKeyService[T any](name string, key string) { type ICommitService[T any] interface { Latest(ctx context.Context, target string, key string) (*Commit[T], error) - ListLatest(ctx context.Context, target ...string) ([]*Commit[T], error) + ListLatest(ctx context.Context, key string, target ...string) ([]*Commit[T], error) Save(ctx context.Context, target string, key string, data *T) error Get(ctx context.Context, uuid string) (*Commit[T], error) List(ctx context.Context, uuids ...string) ([]*Commit[T], error) diff --git a/service/upstream/iml.go b/service/upstream/iml.go index 18308483..830bd8e4 100644 --- a/service/upstream/iml.go +++ b/service/upstream/iml.go @@ -50,7 +50,7 @@ func (i *imlUpstreamService) ListCommit(ctx context.Context, uuid ...string) ([] return i.commitService.List(ctx, uuid...) } -func (i *imlUpstreamService) ListLatestCommit(ctx context.Context, serviceIds ...string) ([]*commit.Commit[Config], error) { +func (i *imlUpstreamService) ListLatestCommit(ctx context.Context, clusterId string, serviceIds ...string) ([]*commit.Commit[Config], error) { w := make(map[string]interface{}) if len(serviceIds) > 0 { w["service"] = serviceIds @@ -67,7 +67,7 @@ func (i *imlUpstreamService) ListLatestCommit(ctx context.Context, serviceIds .. targetId := utils.SliceToSlice(upstreams, func(u *upstream.Upstream) string { return u.UUID }) - return i.commitService.ListLatest(ctx, targetId...) + return i.commitService.ListLatest(ctx, clusterId, targetId...) } diff --git a/service/upstream/service.go b/service/upstream/service.go index 01e48a9a..0204adc6 100644 --- a/service/upstream/service.go +++ b/service/upstream/service.go @@ -14,7 +14,7 @@ type IUpstreamService interface { Delete(ctx context.Context, id string) 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) + ListLatestCommit(ctx context.Context, clusterId string, 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) diff --git a/stores/strategy/model.go b/stores/strategy/model.go new file mode 100644 index 00000000..7be1799a --- /dev/null +++ b/stores/strategy/model.go @@ -0,0 +1,30 @@ +package strategy + +import "time" + +type Strategy 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"` + Priority int `gorm:"type:tinyint(4);not null;column:priority;comment:优先级"` + Desc string `gorm:"type:text;null;column:desc;comment:描述"` + Filters string `gorm:"type:mediumtext;null;column:filters;comment:筛选条件"` + Config string `gorm:"type:mediumtext;null;column:config;comment:配置"` + Driver string `gorm:"type:varchar(100);not null;column:driver;comment:驱动"` + Scope int `gorm:"type:tinyint(1);not null;column:scope;comment:范围 0:全局 1:团队 2:服务"` + Target string `gorm:"type:varchar(36);null;column:target;comment:目标ID"` + Creator string `gorm:"type:varchar(36);not null;column:creator;comment:创建人" aovalue:"creator"` + Updater string `gorm:"type:varchar(36);null;column:updater;comment:更新人" aovalue:"updater"` + CreateAt time.Time `gorm:"type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP;column:create_at;comment:创建时间"` + UpdateAt time.Time `gorm:"type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP;column:update_at;comment:更新时间"` + IsStop bool `gorm:"type:tinyint(1);not null;column:enable;comment:是否禁用 0:否 1:是"` + IsDelete bool `gorm:"type:tinyint(1);not null;column:is_delete;comment:是否删除 0:未删除 1:已删除"` +} + +func (s *Strategy) TableName() string { + return "strategy" +} + +func (s *Strategy) IdValue() int64 { + return s.Id +} diff --git a/stores/strategy/store.go b/stores/strategy/store.go new file mode 100644 index 00000000..6838c688 --- /dev/null +++ b/stores/strategy/store.go @@ -0,0 +1,20 @@ +package strategy + +import ( + "reflect" + + "github.com/eolinker/go-common/autowire" + "github.com/eolinker/go-common/store" +) + +type IStrategyStore store.ISearchStore[Strategy] + +type imlStrategyStore struct { + store.SearchStore[Strategy] +} + +func init() { + autowire.Auto[IStrategyStore](func() reflect.Value { + return reflect.ValueOf(new(imlStrategyStore)) + }) +} From cb63931f30dd62f6710e5e8a2bae42f15e0ec33b Mon Sep 17 00:00:00 2001 From: Liujian <824010343@qq.com> Date: Tue, 26 Nov 2024 23:44:00 +0800 Subject: [PATCH 03/14] finish data mask strategy --- controller/monitor/iml.go | 64 +++++ controller/monitor/statistic.go | 7 +- controller/service/iml.go | 56 ++-- controller/service/service.go | 8 +- controller/strategy/iml.go | 116 +++++++- controller/strategy/strategy.go | 10 +- module/monitor/dto/input.go | 2 +- module/monitor/dto/output.go | 6 +- module/monitor/iml.go | 458 ++++++++++++++++++++++++++++++- module/monitor/monitor.go | 26 +- module/router/dto/output.go | 3 +- module/router/filter.go | 112 ++++++++ module/router/router.go | 8 +- module/service/filter.go | 125 +++++++++ module/service/iml.go | 86 +++--- module/service/module.go | 14 +- module/strategy/driver/filter.go | 132 --------- module/strategy/dto/enum.go | 8 +- module/strategy/dto/output.go | 47 +++- module/strategy/iml.go | 77 +++++- module/strategy/module.go | 3 +- module/system/dto/input.go | 1 + module/system/dto/output.go | 1 + plugins/core/monitor.go | 5 + plugins/core/service.go | 2 + plugins/core/strategy.go | 16 +- resources/locale/i18n/zh-CN.json | 10 +- service/api/iml.go | 24 ++ service/api/service.go | 2 + service/strategy/iml.go | 20 +- service/strategy/service.go | 3 +- stores/strategy/model.go | 2 +- strategy-filter/filter.go | 192 +++++++++++++ strategy-filter/ip.go | 58 ++++ strategy-filter/method.go | 60 ++++ strategy-filter/path.go | 58 ++++ 36 files changed, 1557 insertions(+), 265 deletions(-) create mode 100644 module/router/filter.go create mode 100644 module/service/filter.go delete mode 100644 module/strategy/driver/filter.go create mode 100644 strategy-filter/filter.go create mode 100644 strategy-filter/ip.go create mode 100644 strategy-filter/method.go create mode 100644 strategy-filter/path.go diff --git a/controller/monitor/iml.go b/controller/monitor/iml.go index 26cdbb9e..f1b26ada 100644 --- a/controller/monitor/iml.go +++ b/controller/monitor/iml.go @@ -17,6 +17,70 @@ type imlMonitorStatisticController struct { module monitor.IMonitorStatisticModule `autowired:""` } +func (i *imlMonitorStatisticController) Statistics(ctx *gin.Context, dataType string, input *monitor_dto.StatisticInput) (interface{}, error) { + switch dataType { + case monitor_dto.DataTypeApi: + return i.module.ApiStatistics(ctx, input) + case monitor_dto.DataTypeProvider: + return i.module.ProviderStatistics(ctx, input) + case monitor_dto.DataTypeSubscriber: + return i.module.SubscriberStatistics(ctx, input) + default: + return nil, fmt.Errorf("unsupported data type: %s", dataType) + } +} + +func (i *imlMonitorStatisticController) InvokeTrend(ctx *gin.Context, dataType string, id string, input *monitor_dto.CommonInput) (*monitor_dto.MonInvokeCountTrend, string, error) { + switch dataType { + case monitor_dto.DataTypeApi: + return i.module.APITrend(ctx, id, input) + case monitor_dto.DataTypeProvider: + return i.module.ProviderTrend(ctx, id, input) + case monitor_dto.DataTypeSubscriber: + return i.module.SubscriberTrend(ctx, id, input) + default: + return nil, "", fmt.Errorf("unsupported data type: %s", dataType) + } +} + +func (i *imlMonitorStatisticController) InvokeTrendInner(ctx *gin.Context, dataType string, typ string, api string, provider string, subscriber string, input *monitor_dto.CommonInput) (*monitor_dto.MonInvokeCountTrend, string, error) { + if dataType == monitor_dto.DataTypeApi && typ == monitor_dto.DataTypeSubscriber || dataType == monitor_dto.DataTypeSubscriber && typ == monitor_dto.DataTypeApi { + return i.module.InvokeTrendWithSubscriberAndApi(ctx, api, subscriber, input) + } else if dataType == monitor_dto.DataTypeApi && typ == monitor_dto.DataTypeProvider || dataType == monitor_dto.DataTypeProvider && typ == monitor_dto.DataTypeApi { + return i.module.InvokeTrendWithProviderAndApi(ctx, provider, api, input) + } + return nil, "", fmt.Errorf("unsupported detail type: %s, data type is %s", typ, dataType) +} + +func (i *imlMonitorStatisticController) StatisticsInner(ctx *gin.Context, dataType string, typ string, id string, input *monitor_dto.StatisticInput) (interface{}, error) { + switch dataType { + case monitor_dto.DataTypeApi: + switch typ { + case monitor_dto.DataTypeProvider: + return i.module.ProviderStatisticsOnApi(ctx, id, input) + case monitor_dto.DataTypeSubscriber: + return i.module.SubscriberStatisticsOnApi(ctx, id, input) + default: + return nil, fmt.Errorf("unsupported detail type: %s, data type is %s", typ, dataType) + } + case monitor_dto.DataTypeProvider: + switch typ { + case monitor_dto.DataTypeApi: + return i.module.ApiStatisticsOnProvider(ctx, id, input) + default: + return nil, fmt.Errorf("unsupported detail type: %s, data type is %s", typ, dataType) + } + case monitor_dto.DataTypeSubscriber: + switch typ { + case monitor_dto.DataTypeApi: + return i.module.ApiStatisticsOnSubscriber(ctx, id, input) + default: + return nil, fmt.Errorf("unsupported detail type: %s, data type is %s", typ, dataType) + } + } + return nil, fmt.Errorf("unsupported data type: %s", dataType) +} + func (i *imlMonitorStatisticController) OverviewMessageTrend(ctx *gin.Context, input *monitor_dto.CommonInput) ([]time.Time, []float64, []float64, string, error) { trend, timeInterval, err := i.module.MessageTrend(ctx, input) if err != nil { diff --git a/controller/monitor/statistic.go b/controller/monitor/statistic.go index 33624b87..ca991f68 100644 --- a/controller/monitor/statistic.go +++ b/controller/monitor/statistic.go @@ -16,7 +16,12 @@ type IMonitorStatisticController interface { OverviewInvokeTrend(ctx *gin.Context, input *monitor_dto.CommonInput) ([]time.Time, []int64, []int64, []int64, []int64, []float64, []float64, string, error) OverviewMessageTrend(ctx *gin.Context, input *monitor_dto.CommonInput) ([]time.Time, []float64, []float64, string, error) - //Statistics(ctx *gin.Context, dataType string, input *monitor_dto.StatisticInput) (interface{}, error) + Statistics(ctx *gin.Context, dataType string, input *monitor_dto.StatisticInput) (interface{}, error) + + InvokeTrend(ctx *gin.Context, dataType string, id string, input *monitor_dto.CommonInput) (*monitor_dto.MonInvokeCountTrend, string, error) + + InvokeTrendInner(ctx *gin.Context, dataType string, typ string, api string, provider string, subscriber string, input *monitor_dto.CommonInput) (*monitor_dto.MonInvokeCountTrend, string, error) + StatisticsInner(ctx *gin.Context, dataType string, typ string, id string, input *monitor_dto.StatisticInput) (interface{}, error) } type IMonitorConfigController interface { diff --git a/controller/service/iml.go b/controller/service/iml.go index 0c6bcbb9..1a5e024f 100644 --- a/controller/service/iml.go +++ b/controller/service/iml.go @@ -7,6 +7,8 @@ import ( "strings" "time" + upstream_dto "github.com/APIParkLab/APIPark/module/upstream/dto" + "github.com/eolinker/eosc/log" application_authorization "github.com/APIParkLab/APIPark/module/application-authorization" @@ -25,7 +27,6 @@ import ( "github.com/APIParkLab/APIPark/module/service" service_dto "github.com/APIParkLab/APIPark/module/service/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" "github.com/google/uuid" @@ -47,24 +48,12 @@ type imlServiceController struct { transaction store.ITransaction `autowired:""` } -func newAIUpstream(id string, provider string, uri model_runtime.IProviderURI) *upstream_dto.Upstream { - return &upstream_dto.Upstream{ - Type: "http", - Balance: "round-robin", - Timeout: 300000, - Retry: 0, - Remark: fmt.Sprintf("auto create by ai service %s,provider is %s", id, provider), - LimitPeerSecond: 0, - ProxyHeaders: nil, - Scheme: uri.Scheme(), - PassHost: "node", - Nodes: []*upstream_dto.NodeConfig{ - { - Address: uri.Host(), - Weight: 100, - }, - }, - } +func (i *imlServiceController) Simple(ctx *gin.Context) ([]*service_dto.SimpleServiceItem, error) { + return i.module.Simple(ctx) +} + +func (i *imlServiceController) MySimple(ctx *gin.Context) ([]*service_dto.SimpleServiceItem, error) { + return i.module.MySimple(ctx) } func (i *imlServiceController) editAIService(ctx *gin.Context, id string, input *service_dto.EditService) (*service_dto.Service, error) { @@ -228,21 +217,12 @@ func (i *imlServiceController) SearchMyServices(ctx *gin.Context, teamId string, return i.module.SearchMyServices(ctx, teamId, keyword) } -//func (i *imlServiceController) Simple(ctx *gin.Context, keyword string) ([]*service_dto.SimpleServiceItem, error) { -// return i.module.Simple(ctx, keyword) -//} -// -//func (i *imlServiceController) MySimple(ctx *gin.Context, keyword string) ([]*service_dto.SimpleServiceItem, error) { -// return i.module.MySimple(ctx, keyword) -//} - func (i *imlServiceController) Get(ctx *gin.Context, id string) (*service_dto.Service, error) { now := time.Now() defer func() { log.Infof("get service %s cost %d ms", id, time.Since(now).Milliseconds()) }() return i.module.Get(ctx, id) - } func (i *imlServiceController) Search(ctx *gin.Context, teamID string, keyword string) ([]*service_dto.ServiceItem, error) { @@ -336,3 +316,23 @@ func (i *imlAppController) GetApp(ctx *gin.Context, appId string) (*service_dto. func (i *imlAppController) DeleteApp(ctx *gin.Context, appId string) error { return i.module.DeleteApp(ctx, appId) } + +func newAIUpstream(id string, provider string, uri model_runtime.IProviderURI) *upstream_dto.Upstream { + return &upstream_dto.Upstream{ + Type: "http", + Balance: "round-robin", + Timeout: 300000, + Retry: 0, + Remark: fmt.Sprintf("auto create by ai service %s,provider is %s", id, provider), + LimitPeerSecond: 0, + ProxyHeaders: nil, + Scheme: uri.Scheme(), + PassHost: "node", + Nodes: []*upstream_dto.NodeConfig{ + { + Address: uri.Host(), + Weight: 100, + }, + }, + } +} diff --git a/controller/service/service.go b/controller/service/service.go index e64354e3..6f631781 100644 --- a/controller/service/service.go +++ b/controller/service/service.go @@ -24,12 +24,8 @@ type IServiceController interface { Delete(ctx *gin.Context, id string) error ServiceDoc(ctx *gin.Context, id string) (*service_dto.ServiceDoc, error) SaveServiceDoc(ctx *gin.Context, id string, input *service_dto.SaveServiceDoc) error - - //createAIService(ctx *gin.Context, teamID string, input *service_dto.CreateService) (*service_dto.Service, error) - //editAIService(ctx *gin.Context, id string, input *service_dto.EditService) (*service_dto.Service, error) - //DeleteAIService(ctx *gin.Context, id string) error - //SearchMyAIServices(ctx *gin.Context, teamID string, keyword string) ([]*service_dto.ServiceItem, error) - //SearchAIServices(ctx *gin.Context, teamID string, keyword string) ([]*service_dto.ServiceItem, error) + Simple(ctx *gin.Context) ([]*service_dto.SimpleServiceItem, error) + MySimple(ctx *gin.Context) ([]*service_dto.SimpleServiceItem, error) } type IAppController interface { diff --git a/controller/strategy/iml.go b/controller/strategy/iml.go index c8ed8c59..94786b07 100644 --- a/controller/strategy/iml.go +++ b/controller/strategy/iml.go @@ -1,9 +1,16 @@ package strategy import ( + "encoding/base64" "encoding/json" "fmt" + "sort" "strconv" + "time" + + "github.com/eolinker/go-common/utils" + + strategy_filter "github.com/APIParkLab/APIPark/strategy-filter" "github.com/APIParkLab/APIPark/module/service" "github.com/APIParkLab/APIPark/module/strategy" @@ -18,6 +25,105 @@ type imlStrategyController struct { serviceModule service.IServiceModule `autowired:""` } +func (i *imlStrategyController) ToPublish(ctx *gin.Context, driver string) ([]*strategy_dto.ToPublishItem, string, string, bool, error) { + list, err := i.strategyModule.ToPublish(ctx, driver) + if err != nil { + return nil, "", "", false, err + } + data, _ := json.Marshal(list) + source := base64.StdEncoding.EncodeToString(data) + return list, source, time.Now().Format("20060102150405") + "-release", len(list) > 0, nil +} + +func (i *imlStrategyController) FilterGlobalRemote(ctx *gin.Context, name string) ([]*strategy_dto.Title, []any, int64, string, string, error) { + f, has := strategy_filter.RemoteFilter(name) + if !has { + return nil, nil, 0, "", "", fmt.Errorf("filter not found: %s", name) + } + scopeAllow := false + for _, s := range f.Scopes() { + if s == strategy_filter.ScopeGlobal { + scopeAllow = true + break + } + } + if !scopeAllow { + return nil, nil, 0, "", "", fmt.Errorf("scope not allowed: %s", name) + } + + list, total, err := f.RemoteList(ctx, "", nil, -1, -1) + if err != nil { + return nil, nil, 0, "", "", err + } + return utils.SliceToSlice(f.Titles(), func(l strategy_filter.OptionTitle) *strategy_dto.Title { + return &strategy_dto.Title{ + Field: l.Field, + Title: l.Title, + } + }), list, total, f.Key(), f.Key(), nil +} + +func (i *imlStrategyController) FilterServiceRemote(ctx *gin.Context, serviceId string, name string) ([]*strategy_dto.Title, []any, int64, string, string, error) { + f, has := strategy_filter.RemoteFilter(name) + if !has { + return nil, nil, 0, "", "", fmt.Errorf("filter not found: %s", name) + } + scopeAllow := false + for _, s := range f.Scopes() { + if s == strategy_filter.ScopeService { + scopeAllow = true + break + } + } + if !scopeAllow { + return nil, nil, 0, "", "", fmt.Errorf("scope not allowed: %s", name) + } + list, total, err := f.RemoteList(ctx, "", map[string]interface{}{"service": serviceId}, -1, -1) + if err != nil { + return nil, nil, 0, "", "", err + } + return utils.SliceToSlice(f.Titles(), func(l strategy_filter.OptionTitle) *strategy_dto.Title { + return &strategy_dto.Title{ + Field: l.Field, + Title: l.Title, + } + }), list, total, f.Key(), "list", nil + +} + +func (i *imlStrategyController) filterOptions(ctx *gin.Context, scope string) ([]*strategy_dto.FilterOption, error) { + m, has := strategy_filter.Options(scope) + if !has { + return nil, fmt.Errorf("scope not found: %s", scope) + } + + list := utils.MapToSlice(m, func(key string, value *strategy_filter.Option) *strategy_dto.FilterOption { + pattern := "" + if value.Pattern != nil { + pattern = value.Pattern.String() + } + return &strategy_dto.FilterOption{ + Name: value.Name, + Title: value.Title, + Type: value.Type, + Pattern: pattern, + Options: value.Options, + } + }) + sort.Slice(list, func(i, j int) bool { + return list[i].Name < list[j].Name + }) + return list, nil +} + +func (i *imlStrategyController) FilterServiceOptions(ctx *gin.Context) ([]*strategy_dto.FilterOption, error) { + return i.filterOptions(ctx, strategy_filter.ScopeService) +} + +func (i *imlStrategyController) FilterGlobalOptions(ctx *gin.Context) ([]*strategy_dto.FilterOption, error) { + return i.filterOptions(ctx, strategy_filter.ScopeGlobal) +} + func (i *imlStrategyController) GetStrategy(ctx *gin.Context, id string) (*strategy_dto.Strategy, error) { return i.strategyModule.Get(ctx, id) } @@ -51,18 +157,18 @@ func (i *imlStrategyController) search(ctx *gin.Context, keyword string, scope s func (i *imlStrategyController) GlobalStrategyList(ctx *gin.Context, keyword string, driver string, page string, pageSize string, order string, sort string, filters string) ([]*strategy_dto.StrategyItem, int64, error) { - return i.search(ctx, keyword, strategy_dto.ToScope(strategy_dto.ScopeSystem), "", driver, page, pageSize, order, sort, filters) + return i.search(ctx, keyword, strategy_dto.ToScope(strategy_dto.ScopeGlobal), "", driver, page, pageSize, order, sort, filters) } func (i *imlStrategyController) CreateGlobalStrategy(ctx *gin.Context, driver string, input *strategy_dto.Create) error { input.Driver = driver - input.Scope = strategy_dto.ToScope(strategy_dto.ScopeSystem) + input.Scope = strategy_dto.ToScope(strategy_dto.ScopeGlobal) return i.strategyModule.Create(ctx, input) } -func (i *imlStrategyController) PublishGlobalStrategy(ctx *gin.Context) error { - return i.strategyModule.Publish(ctx, strategy_dto.ScopeSystem, "") +func (i *imlStrategyController) PublishGlobalStrategy(ctx *gin.Context, driver string) error { + return i.strategyModule.Publish(ctx, driver, strategy_dto.ScopeGlobal, "") } func (i *imlStrategyController) ServiceStrategyList(ctx *gin.Context, keyword string, serviceId string, driver string, page string, pageSize string, order string, sort string, filters string) ([]*strategy_dto.StrategyItem, int64, error) { @@ -87,7 +193,7 @@ func (i *imlStrategyController) EditStrategy(ctx *gin.Context, id string, input } func (i *imlStrategyController) EnableStrategy(ctx *gin.Context, id string) error { - return i.EnableStrategy(ctx, id) + return i.strategyModule.Enable(ctx, id) } func (i *imlStrategyController) DisableStrategy(ctx *gin.Context, id string) error { diff --git a/controller/strategy/strategy.go b/controller/strategy/strategy.go index 8dbb04fb..b6eac85a 100644 --- a/controller/strategy/strategy.go +++ b/controller/strategy/strategy.go @@ -11,7 +11,7 @@ import ( type IStrategyController interface { GlobalStrategyList(ctx *gin.Context, keyword string, driver string, page string, pageSize string, order string, sort string, filters string) ([]*strategy_dto.StrategyItem, int64, error) CreateGlobalStrategy(ctx *gin.Context, driver string, input *strategy_dto.Create) error - PublishGlobalStrategy(ctx *gin.Context) error + PublishGlobalStrategy(ctx *gin.Context, driver string) error ServiceStrategyList(ctx *gin.Context, keyword string, serviceId string, driver string, page string, pageSize string, order string, sort string, filters string) ([]*strategy_dto.StrategyItem, int64, error) CreateServiceStrategy(ctx *gin.Context, serviceId string, driver string, input *strategy_dto.Create) error @@ -22,6 +22,14 @@ type IStrategyController interface { DisableStrategy(ctx *gin.Context, id string) error DeleteStrategy(ctx *gin.Context, id string) error + + FilterGlobalOptions(ctx *gin.Context) ([]*strategy_dto.FilterOption, error) + FilterServiceOptions(ctx *gin.Context) ([]*strategy_dto.FilterOption, error) + + FilterGlobalRemote(ctx *gin.Context, name string) ([]*strategy_dto.Title, []any, int64, string, string, error) + FilterServiceRemote(ctx *gin.Context, serviceId string, name string) ([]*strategy_dto.Title, []any, int64, string, string, error) + + ToPublish(ctx *gin.Context, driver string) ([]*strategy_dto.ToPublishItem, string, string, bool, error) } type IStrategyCommonController interface { diff --git a/module/monitor/dto/input.go b/module/monitor/dto/input.go index 7de6a169..7d01b8bd 100644 --- a/module/monitor/dto/input.go +++ b/module/monitor/dto/input.go @@ -24,7 +24,7 @@ type CommonInput struct { type StatisticInput struct { Apis []string `json:"apis"` - Projects []string `json:"projects"` + Services []string `json:"services"` Path string `json:"path"` *CommonInput } diff --git a/module/monitor/dto/output.go b/module/monitor/dto/output.go index 0b787bf8..5f6b1660 100644 --- a/module/monitor/dto/output.go +++ b/module/monitor/dto/output.go @@ -21,12 +21,12 @@ type ApiStatisticBasicItem struct { *MonCommonData } -type ProjectStatisticItem struct { - *ProjectStatisticBasicItem +type ServiceStatisticItem struct { + *ServiceStatisticBasicItem IsRed bool `json:"is_red"` //是否标红 } -type ProjectStatisticBasicItem struct { +type ServiceStatisticBasicItem struct { Id string `json:"id"` //订阅方ID Name string `json:"name"` //订阅方名称 *MonCommonData diff --git a/module/monitor/iml.go b/module/monitor/iml.go index 0179b1ec..6e497a49 100644 --- a/module/monitor/iml.go +++ b/module/monitor/iml.go @@ -5,14 +5,15 @@ import ( "encoding/json" "errors" "fmt" + "sort" + "time" + "github.com/APIParkLab/APIPark/gateway" "github.com/eolinker/eosc/log" "github.com/eolinker/go-common/auto" "github.com/eolinker/go-common/store" "github.com/eolinker/go-common/utils" "gorm.io/gorm" - "sort" - "time" "github.com/APIParkLab/APIPark/service/service" @@ -42,6 +43,442 @@ type imlMonitorStatisticModule struct { apiService api.IAPIService `autowired:""` } +func (i *imlMonitorStatisticModule) ApiStatistics(ctx context.Context, input *monitor_dto.StatisticInput) ([]*monitor_dto.ApiStatisticBasicItem, error) { + clusterId := cluster.DefaultClusterID + _, err := i.clusterService.Get(ctx, clusterId) + if err != nil { + return nil, err + } + wheres, err := i.genCommonWheres(ctx, clusterId) + if err != nil { + return nil, err + } + + wm := make(map[string]interface{}) + if len(input.Apis) > 0 { + wm["uuid"] = input.Apis + wheres = append(wheres, monitor.MonWhereItem{ + Key: "api", + Operation: "in", + Values: input.Apis, + }) + } + if len(input.Services) > 0 { + wm["service"] = input.Services + wheres = append(wheres, monitor.MonWhereItem{ + Key: "project", + Operation: "in", + Values: input.Services, + }) + } + // 查询符合条件的API + apis, err := i.apiService.Search(ctx, input.Path, wm) + if err != nil { + return nil, err + } + if len(apis) < 1 { + // 没有符合条件的API + return make([]*monitor_dto.ApiStatisticBasicItem, 0), nil + } + apiIds := utils.SliceToSlice(apis, func(t *api.API) string { + return t.UUID + }) + + apiInfos, err := i.apiService.ListInfo(ctx, apiIds...) + if err != nil { + return nil, err + } + return i.apiStatistics(ctx, clusterId, apiInfos, formatTimeByMinute(input.Start), formatTimeByMinute(input.End), wheres, 0) +} + +func (i *imlMonitorStatisticModule) apiStatistics(ctx context.Context, clusterId string, apiInfos []*api.Info, start time.Time, end time.Time, wheres []monitor.MonWhereItem, limit int) ([]*monitor_dto.ApiStatisticBasicItem, error) { + statisticMap, err := i.statistics(ctx, clusterId, "api", start, end, wheres, limit) + if err != nil { + return nil, err + } + + result := make([]*monitor_dto.ApiStatisticBasicItem, 0, len(statisticMap)) + for _, item := range apiInfos { + + statisticItem := &monitor_dto.ApiStatisticBasicItem{ + Id: item.UUID, + Name: item.Name, + Path: item.Path, + Service: auto.UUID(item.Service), + MonCommonData: new(monitor_dto.MonCommonData), + } + if val, ok := statisticMap[item.UUID]; ok { + statisticItem.MonCommonData = monitor_dto.ToMonCommonData(val) + delete(statisticMap, item.UUID) + } + result = append(result, statisticItem) + } + for key, item := range statisticMap { + statisticItem := &monitor_dto.ApiStatisticBasicItem{ + Id: key, + Name: "未知API-" + key, + MonCommonData: monitor_dto.ToMonCommonData(item), + } + + if key == "-" { + statisticItem.Name = "无API" + } + result = append(result, statisticItem) + } + sort.Slice(result, func(i, j int) bool { + return result[i].RequestTotal > result[j].RequestTotal + }) + return result, nil +} + +func (i *imlMonitorStatisticModule) SubscriberStatistics(ctx context.Context, input *monitor_dto.StatisticInput) ([]*monitor_dto.ServiceStatisticBasicItem, error) { + clusterId := cluster.DefaultClusterID + _, err := i.clusterService.Get(ctx, clusterId) + if err != nil { + return nil, err + } + + apps, err := i.serviceService.AppList(ctx, input.Services...) + if err != nil { + return nil, err + } + appIds := utils.SliceToSlice(apps, func(p *service.Service) string { + return p.Id + }) + + wheres, err := i.genCommonWheres(ctx, clusterId) + if err != nil { + return nil, err + } + + if len(appIds) > 0 { + wheres = append(wheres, monitor.MonWhereItem{ + Key: "app", + Operation: "in", + Values: appIds, + }) + } + + return i.serviceStatistics(ctx, clusterId, apps, "app", formatTimeByMinute(input.Start), formatTimeByMinute(input.End), wheres, 0) +} + +func (i *imlMonitorStatisticModule) serviceStatistics(ctx context.Context, clusterId string, services []*service.Service, groupBy string, start time.Time, end time.Time, wheres []monitor.MonWhereItem, limit int) ([]*monitor_dto.ServiceStatisticBasicItem, error) { + statisticMap, err := i.statistics(ctx, clusterId, groupBy, start, end, wheres, limit) + if err != nil { + return nil, err + } + + result := make([]*monitor_dto.ServiceStatisticBasicItem, 0, len(statisticMap)) + for _, item := range services { + statisticItem := &monitor_dto.ServiceStatisticBasicItem{ + Id: item.Id, + Name: item.Name, + MonCommonData: new(monitor_dto.MonCommonData), + } + if val, ok := statisticMap[item.Id]; ok { + statisticItem.MonCommonData = monitor_dto.ToMonCommonData(val) + delete(statisticMap, item.Id) + } + result = append(result, statisticItem) + } + for key, item := range statisticMap { + statisticItem := &monitor_dto.ServiceStatisticBasicItem{ + Id: key, + Name: "未知-" + key, + MonCommonData: monitor_dto.ToMonCommonData(item), + } + + if key == "-" { + statisticItem.Name = "-" + } + result = append(result, statisticItem) + } + sort.Slice(result, func(i, j int) bool { + return result[i].RequestTotal > result[j].RequestTotal + }) + return result, nil +} + +func (i *imlMonitorStatisticModule) ProviderStatistics(ctx context.Context, input *monitor_dto.StatisticInput) ([]*monitor_dto.ServiceStatisticBasicItem, error) { + clusterId := cluster.DefaultClusterID + _, err := i.clusterService.Get(ctx, clusterId) + if err != nil { + return nil, err + } + + services, err := i.serviceService.ServiceList(ctx, input.Services...) + if err != nil { + return nil, err + } + + wheres, err := i.genCommonWheres(ctx, clusterId) + if err != nil { + return nil, err + } + + if len(input.Services) > 0 { + wheres = append(wheres, monitor.MonWhereItem{ + Key: "provider", + Operation: "in", + Values: input.Services, + }) + } + + return i.serviceStatistics(ctx, clusterId, services, "provider", formatTimeByMinute(input.Start), formatTimeByMinute(input.End), wheres, 0) +} + +func (i *imlMonitorStatisticModule) APITrend(ctx context.Context, apiId string, input *monitor_dto.CommonInput) (*monitor_dto.MonInvokeCountTrend, string, error) { + clusterId := cluster.DefaultClusterID + wheres, err := i.genCommonWheres(ctx, clusterId) + if err != nil { + return nil, "", err + } + wheres = append(wheres, monitor.MonWhereItem{ + Key: "api", + Operation: "=", + Values: []string{apiId}, + }) + executor, err := i.getExecutor(ctx, clusterId) + if err != nil { + return nil, "", err + } + result, timeInterval, err := executor.InvokeTrend(ctx, formatTimeByMinute(input.Start), formatTimeByMinute(input.End), wheres) + if err != nil { + return nil, "", err + } + return monitor_dto.ToMonInvokeCountTrend(result), timeInterval, nil +} + +func (i *imlMonitorStatisticModule) ProviderTrend(ctx context.Context, providerId string, input *monitor_dto.CommonInput) (*monitor_dto.MonInvokeCountTrend, string, error) { + clusterId := cluster.DefaultClusterID + wheres, err := i.genCommonWheres(ctx, clusterId) + if err != nil { + return nil, "", err + } + wheres = append(wheres, monitor.MonWhereItem{ + Key: "provider", + Operation: "=", + Values: []string{providerId}, + }) + executor, err := i.getExecutor(ctx, clusterId) + if err != nil { + return nil, "", err + } + result, timeInterval, err := executor.InvokeTrend(ctx, formatTimeByMinute(input.Start), formatTimeByMinute(input.End), wheres) + if err != nil { + return nil, "", err + } + return monitor_dto.ToMonInvokeCountTrend(result), timeInterval, nil +} + +func (i *imlMonitorStatisticModule) SubscriberTrend(ctx context.Context, subscriberId string, input *monitor_dto.CommonInput) (*monitor_dto.MonInvokeCountTrend, string, error) { + clusterId := cluster.DefaultClusterID + wheres, err := i.genCommonWheres(ctx, clusterId) + if err != nil { + return nil, "", err + } + wheres = append(wheres, monitor.MonWhereItem{ + Key: "app", + Operation: "=", + Values: []string{subscriberId}, + }) + executor, err := i.getExecutor(ctx, clusterId) + if err != nil { + return nil, "", err + } + result, timeInterval, err := executor.InvokeTrend(ctx, formatTimeByMinute(input.Start), formatTimeByMinute(input.End), wheres) + if err != nil { + return nil, "", err + } + return monitor_dto.ToMonInvokeCountTrend(result), timeInterval, nil +} + +func (i *imlMonitorStatisticModule) InvokeTrendWithSubscriberAndApi(ctx context.Context, apiId string, subscriberId string, input *monitor_dto.CommonInput) (*monitor_dto.MonInvokeCountTrend, string, error) { + clusterId := cluster.DefaultClusterID + wheres, err := i.genCommonWheres(ctx, clusterId) + if err != nil { + return nil, "", err + } + wheres = append(wheres, monitor.MonWhereItem{ + Key: "api", + Operation: "=", + Values: []string{apiId}, + }, monitor.MonWhereItem{ + Key: "app", + Operation: "=", + Values: []string{subscriberId}, + }) + executor, err := i.getExecutor(ctx, clusterId) + if err != nil { + return nil, "", err + } + result, timeInterval, err := executor.InvokeTrend(ctx, formatTimeByMinute(input.Start), formatTimeByMinute(input.End), wheres) + if err != nil { + return nil, "", err + } + return monitor_dto.ToMonInvokeCountTrend(result), timeInterval, nil +} + +func (i *imlMonitorStatisticModule) InvokeTrendWithProviderAndApi(ctx context.Context, providerId string, apiId string, input *monitor_dto.CommonInput) (*monitor_dto.MonInvokeCountTrend, string, error) { + clusterId := cluster.DefaultClusterID + wheres, err := i.genCommonWheres(ctx, clusterId) + if err != nil { + return nil, "", err + } + wheres = append(wheres, monitor.MonWhereItem{ + Key: "api", + Operation: "=", + Values: []string{apiId}, + }, monitor.MonWhereItem{ + Key: "provider", + Operation: "=", + Values: []string{providerId}, + }) + executor, err := i.getExecutor(ctx, clusterId) + if err != nil { + return nil, "", err + } + result, timeInterval, err := executor.InvokeTrend(ctx, formatTimeByMinute(input.Start), formatTimeByMinute(input.End), wheres) + if err != nil { + return nil, "", err + } + return monitor_dto.ToMonInvokeCountTrend(result), timeInterval, nil +} + +func (i *imlMonitorStatisticModule) statisticOnApi(ctx context.Context, clusterId string, apiId string, groupBy string, input *monitor_dto.StatisticInput) ([]*monitor_dto.ServiceStatisticBasicItem, error) { + _, err := i.clusterService.Get(ctx, clusterId) + if err != nil { + return nil, err + } + var service []*service.Service + switch groupBy { + case "app": + service, err = i.serviceService.AppList(ctx) + case "provider": + service, err = i.serviceService.ServiceList(ctx) + default: + return nil, errors.New("invalid group by") + } + if err != nil { + return nil, err + } + + wheres, err := i.genCommonWheres(ctx, clusterId) + if err != nil { + return nil, err + } + wheres = append(wheres, monitor.MonWhereItem{ + Key: "api", + Operation: "=", + Values: []string{apiId}, + }) + + statisticMap, err := i.statistics(ctx, clusterId, groupBy, formatTimeByMinute(input.Start), formatTimeByMinute(input.End), wheres, 0) + if err != nil { + return nil, err + } + + result := make([]*monitor_dto.ServiceStatisticBasicItem, 0, len(statisticMap)) + for _, item := range service { + + statisticItem := &monitor_dto.ServiceStatisticBasicItem{ + Id: item.Id, + Name: item.Name, + MonCommonData: new(monitor_dto.MonCommonData), + } + if val, ok := statisticMap[item.Id]; ok { + statisticItem.MonCommonData = monitor_dto.ToMonCommonData(val) + delete(statisticMap, item.Id) + } + result = append(result, statisticItem) + } + for key, item := range statisticMap { + statisticItem := &monitor_dto.ServiceStatisticBasicItem{ + Id: key, + Name: "未知-" + key, + MonCommonData: monitor_dto.ToMonCommonData(item), + } + + if key == "-" { + statisticItem.Name = "-" + } + result = append(result, statisticItem) + } + sort.Slice(result, func(i, j int) bool { + return result[i].RequestTotal > result[j].RequestTotal + }) + return result, nil +} + +func (i *imlMonitorStatisticModule) ProviderStatisticsOnApi(ctx context.Context, apiId string, input *monitor_dto.StatisticInput) ([]*monitor_dto.ServiceStatisticBasicItem, error) { + clusterId := cluster.DefaultClusterID + return i.statisticOnApi(ctx, clusterId, apiId, "provider", input) +} + +func (i *imlMonitorStatisticModule) ApiStatisticsOnProvider(ctx context.Context, providerId string, input *monitor_dto.StatisticInput) ([]*monitor_dto.ApiStatisticBasicItem, error) { + clusterId := cluster.DefaultClusterID + _, err := i.clusterService.Get(ctx, clusterId) + if err != nil { + return nil, err + } + + apiInfos, err := i.apiService.ListInfoForService(ctx, providerId) + if err != nil { + return nil, err + } + wheres, err := i.genCommonWheres(ctx, clusterId) + if err != nil { + return nil, err + } + wheres = append(wheres, monitor.MonWhereItem{ + Key: "provider", + Operation: "=", + Values: []string{providerId}, + }) + + return i.apiStatistics(ctx, clusterId, apiInfos, formatTimeByMinute(input.Start), formatTimeByMinute(input.End), wheres, 0) +} + +func (i *imlMonitorStatisticModule) ApiStatisticsOnSubscriber(ctx context.Context, subscriberId string, input *monitor_dto.StatisticInput) ([]*monitor_dto.ApiStatisticBasicItem, error) { + clusterId := cluster.DefaultClusterID + _, err := i.clusterService.Get(ctx, clusterId) + if err != nil { + return nil, err + } + // 根据订阅ID查询订阅的服务列表 + subscriptions, err := i.subscribeService.MySubscribeServices(ctx, subscriberId, nil) + if err != nil { + return nil, err + } + serviceIds := utils.SliceToSlice(subscriptions, func(t *subscribe.Subscribe) string { + return t.Service + }) + if len(serviceIds) < 1 { + return nil, nil + } + apiInfos, err := i.apiService.ListInfoForServices(ctx, serviceIds...) + if err != nil { + return nil, err + } + + wheres, err := i.genCommonWheres(ctx, clusterId) + if err != nil { + return nil, err + } + wheres = append(wheres, monitor.MonWhereItem{ + Key: "app", + Operation: "=", + Values: []string{subscriberId}, + }) + + return i.apiStatistics(ctx, clusterId, apiInfos, formatTimeByMinute(input.Start), formatTimeByMinute(input.End), wheres, 0) +} + +func (i *imlMonitorStatisticModule) SubscriberStatisticsOnApi(ctx context.Context, apiId string, input *monitor_dto.StatisticInput) ([]*monitor_dto.ServiceStatisticBasicItem, error) { + clusterId := cluster.DefaultClusterID + return i.statisticOnApi(ctx, clusterId, apiId, "app", input) +} + func (i *imlMonitorStatisticModule) MessageTrend(ctx context.Context, input *monitor_dto.CommonInput) (*monitor_dto.MonMessageTrend, string, error) { clusterId := cluster.DefaultClusterID wheres, err := i.genCommonWheres(ctx, clusterId) @@ -173,7 +610,7 @@ func (i *imlMonitorStatisticModule) TopAPIStatistics(ctx context.Context, limit } -func (i *imlMonitorStatisticModule) TopSubscriberStatistics(ctx context.Context, limit int, input *monitor_dto.CommonInput) ([]*monitor_dto.ProjectStatisticItem, error) { +func (i *imlMonitorStatisticModule) TopSubscriberStatistics(ctx context.Context, limit int, input *monitor_dto.CommonInput) ([]*monitor_dto.ServiceStatisticItem, error) { clusterId := cluster.DefaultClusterID _, err := i.clusterService.Get(ctx, clusterId) if err != nil { @@ -182,7 +619,7 @@ func (i *imlMonitorStatisticModule) TopSubscriberStatistics(ctx context.Context, return i.topProjectStatistics(ctx, clusterId, "app", input, limit) } -func (i *imlMonitorStatisticModule) TopProviderStatistics(ctx context.Context, limit int, input *monitor_dto.CommonInput) ([]*monitor_dto.ProjectStatisticItem, error) { +func (i *imlMonitorStatisticModule) TopProviderStatistics(ctx context.Context, limit int, input *monitor_dto.CommonInput) ([]*monitor_dto.ServiceStatisticItem, error) { clusterId := cluster.DefaultClusterID _, err := i.clusterService.Get(ctx, clusterId) if err != nil { @@ -191,12 +628,12 @@ func (i *imlMonitorStatisticModule) TopProviderStatistics(ctx context.Context, l return i.topProjectStatistics(ctx, clusterId, "provider", input, limit) } -func (i *imlMonitorStatisticModule) topProjectStatistics(ctx context.Context, partitionId string, groupBy string, input *monitor_dto.CommonInput, limit int) ([]*monitor_dto.ProjectStatisticItem, error) { - wheres, err := i.genCommonWheres(ctx, partitionId) +func (i *imlMonitorStatisticModule) topProjectStatistics(ctx context.Context, clusterId string, groupBy string, input *monitor_dto.CommonInput, limit int) ([]*monitor_dto.ServiceStatisticItem, error) { + wheres, err := i.genCommonWheres(ctx, clusterId) if err != nil { return nil, err } - statisticMap, err := i.statistics(ctx, partitionId, groupBy, formatTimeByMinute(input.Start), formatTimeByMinute(input.End), wheres, limit) + statisticMap, err := i.statistics(ctx, clusterId, groupBy, formatTimeByMinute(input.Start), formatTimeByMinute(input.End), wheres, limit) if err != nil { return nil, err } @@ -216,10 +653,10 @@ func (i *imlMonitorStatisticModule) topProjectStatistics(ctx context.Context, pa return t.Id }) - result := make([]*monitor_dto.ProjectStatisticItem, 0, len(statisticMap)) + result := make([]*monitor_dto.ServiceStatisticItem, 0, len(statisticMap)) for key, item := range statisticMap { - statisticItem := &monitor_dto.ProjectStatisticItem{ - ProjectStatisticBasicItem: &monitor_dto.ProjectStatisticBasicItem{ + statisticItem := &monitor_dto.ServiceStatisticItem{ + ServiceStatisticBasicItem: &monitor_dto.ServiceStatisticBasicItem{ Id: key, MonCommonData: monitor_dto.ToMonCommonData(item), }, @@ -407,7 +844,6 @@ func (m *imlMonitorConfig) GetMonitorConfig(ctx context.Context) (*monitor_dto.M Driver: info.Driver, Config: cfg, }, nil - return nil, nil } func (m *imlMonitorConfig) GetMonitorCluster(ctx context.Context) ([]*monitor_dto.MonitorCluster, error) { diff --git a/module/monitor/monitor.go b/module/monitor/monitor.go index 5a676efb..ba897c70 100644 --- a/module/monitor/monitor.go +++ b/module/monitor/monitor.go @@ -13,18 +13,36 @@ import ( type IMonitorStatisticModule interface { TopAPIStatistics(ctx context.Context, limit int, input *monitor_dto.CommonInput) ([]*monitor_dto.ApiStatisticItem, error) - TopProviderStatistics(ctx context.Context, limit int, input *monitor_dto.CommonInput) ([]*monitor_dto.ProjectStatisticItem, error) - TopSubscriberStatistics(ctx context.Context, limit int, input *monitor_dto.CommonInput) ([]*monitor_dto.ProjectStatisticItem, error) + TopProviderStatistics(ctx context.Context, limit int, input *monitor_dto.CommonInput) ([]*monitor_dto.ServiceStatisticItem, error) + TopSubscriberStatistics(ctx context.Context, limit int, input *monitor_dto.CommonInput) ([]*monitor_dto.ServiceStatisticItem, error) // RequestSummary 请求概况 RequestSummary(ctx context.Context, input *monitor_dto.CommonInput) (*monitor_dto.MonSummaryOutput, error) // ProxySummary 转发概况 ProxySummary(ctx context.Context, input *monitor_dto.CommonInput) (*monitor_dto.MonSummaryOutput, error) - // InvokeTrend 调用次数趋势 InvokeTrend(ctx context.Context, input *monitor_dto.CommonInput) (*monitor_dto.MonInvokeCountTrend, string, error) - // MessageTrend 消息趋势 MessageTrend(ctx context.Context, input *monitor_dto.CommonInput) (*monitor_dto.MonMessageTrend, string, error) + + ApiStatistics(ctx context.Context, input *monitor_dto.StatisticInput) ([]*monitor_dto.ApiStatisticBasicItem, error) + + SubscriberStatistics(ctx context.Context, input *monitor_dto.StatisticInput) ([]*monitor_dto.ServiceStatisticBasicItem, error) + + ProviderStatistics(ctx context.Context, input *monitor_dto.StatisticInput) ([]*monitor_dto.ServiceStatisticBasicItem, error) + + APITrend(ctx context.Context, apiId string, input *monitor_dto.CommonInput) (*monitor_dto.MonInvokeCountTrend, string, error) + + ProviderTrend(ctx context.Context, providerId string, input *monitor_dto.CommonInput) (*monitor_dto.MonInvokeCountTrend, string, error) + + SubscriberTrend(ctx context.Context, subscriberId string, input *monitor_dto.CommonInput) (*monitor_dto.MonInvokeCountTrend, string, error) + + InvokeTrendWithSubscriberAndApi(ctx context.Context, apiId string, subscriberId string, input *monitor_dto.CommonInput) (*monitor_dto.MonInvokeCountTrend, string, error) + InvokeTrendWithProviderAndApi(ctx context.Context, providerId string, apiId string, input *monitor_dto.CommonInput) (*monitor_dto.MonInvokeCountTrend, string, error) + + ProviderStatisticsOnApi(ctx context.Context, apiId string, input *monitor_dto.StatisticInput) ([]*monitor_dto.ServiceStatisticBasicItem, error) + ApiStatisticsOnProvider(ctx context.Context, providerId string, input *monitor_dto.StatisticInput) ([]*monitor_dto.ApiStatisticBasicItem, error) + ApiStatisticsOnSubscriber(ctx context.Context, subscriberId string, input *monitor_dto.StatisticInput) ([]*monitor_dto.ApiStatisticBasicItem, error) + SubscriberStatisticsOnApi(ctx context.Context, apiId string, input *monitor_dto.StatisticInput) ([]*monitor_dto.ServiceStatisticBasicItem, error) } type IMonitorConfigModule interface { diff --git a/module/router/dto/output.go b/module/router/dto/output.go index 53988c2b..45fe4888 100644 --- a/module/router/dto/output.go +++ b/module/router/dto/output.go @@ -26,7 +26,8 @@ type Item struct { type SimpleItem struct { Id string `json:"id"` Methods []string `json:"methods"` - Path string `json:"request_path"` + //Name string `json:"name"` + Path string `json:"request_path"` } type Detail struct { diff --git a/module/router/filter.go b/module/router/filter.go new file mode 100644 index 00000000..5c4c47a0 --- /dev/null +++ b/module/router/filter.go @@ -0,0 +1,112 @@ +package router + +import ( + "context" + + router_dto "github.com/APIParkLab/APIPark/module/router/dto" + "github.com/eolinker/go-common/utils" + + "github.com/eolinker/eosc/log" + + "github.com/APIParkLab/APIPark/service/api" + strategy_filter "github.com/APIParkLab/APIPark/strategy-filter" +) + +var _ strategy_filter.IRemoteFilter = (*imlRouterFilter)(nil) + +type imlRouterFilter struct { + service api.IAPIService `autowired:""` +} + +func (i *imlRouterFilter) Name() string { + return "api" +} + +func (i *imlRouterFilter) Title() string { + return "API" +} + +func (i *imlRouterFilter) Labels(values ...string) []string { + list, err := i.service.ListInfo(context.Background(), values...) + if err != nil { + log.Errorf("get api labels error: %v", err) + return nil + } + + return utils.SliceToSlice(list, func(a *api.Info) string { + return a.Name + }) +} + +func (i *imlRouterFilter) Type() string { + return strategy_filter.TypeRemote +} + +func (i *imlRouterFilter) Scopes() []string { + return []string{ + //strategy_filter.ScopeGlobal, + strategy_filter.ScopeService, + } +} + +func (i *imlRouterFilter) Option() *strategy_filter.Option { + return &strategy_filter.Option{ + Name: i.Name(), + Title: i.Title(), + Type: i.Type(), + } +} + +func (i *imlRouterFilter) Titles() []strategy_filter.OptionTitle { + return []strategy_filter.OptionTitle{ + { + Field: "name", + Title: "api name", + }, + { + Field: "methods", + Title: "methods", + }, + { + Field: "request_path", + Title: "request path", + }, + } +} + +func (i *imlRouterFilter) Key() string { + return "id" +} + +func (i *imlRouterFilter) Target() string { + return "list" +} + +func (i *imlRouterFilter) RemoteList(ctx context.Context, keyword string, condition map[string]interface{}, page int, pageSize int) ([]any, int64, error) { + if pageSize == -1 { + // 获取全部 + list, err := i.service.Search(ctx, keyword, condition) + if err != nil { + return nil, 0, err + } + + return utils.SliceToSlice(list, func(s *api.API) any { + return &router_dto.SimpleItem{ + Id: s.UUID, + Path: s.Path, + Methods: s.Method, + } + }), int64(len(list)), nil + } + list, total, err := i.service.SearchByPage(ctx, keyword, condition, page, pageSize, "update_at") + if err != nil { + return nil, 0, err + } + return utils.SliceToSlice(list, func(s *api.API) any { + return &router_dto.SimpleItem{ + Id: s.UUID, + Path: s.Path, + Methods: s.Method, + } + }), total, nil +} diff --git a/module/router/router.go b/module/router/router.go index 1a43b958..36c83273 100644 --- a/module/router/router.go +++ b/module/router/router.go @@ -2,9 +2,11 @@ package router import ( "context" - "github.com/APIParkLab/APIPark/module/system" "reflect" + "github.com/APIParkLab/APIPark/module/system" + strategy_filter "github.com/APIParkLab/APIPark/strategy-filter" + "github.com/eolinker/go-common/autowire" router_dto "github.com/APIParkLab/APIPark/module/router/dto" @@ -45,4 +47,8 @@ func init() { autowire.Auto[IExportRouterModule](func() reflect.Value { return reflect.ValueOf(apiModule) }) + + filter := new(imlRouterFilter) + autowire.Autowired(filter) + strategy_filter.RegisterRemoteFilter(filter) } diff --git a/module/service/filter.go b/module/service/filter.go new file mode 100644 index 00000000..4304a266 --- /dev/null +++ b/module/service/filter.go @@ -0,0 +1,125 @@ +package service + +import ( + "context" + + service_dto "github.com/APIParkLab/APIPark/module/service/dto" + "github.com/eolinker/go-common/auto" + + "github.com/APIParkLab/APIPark/service/service" + "github.com/eolinker/eosc/log" + "github.com/eolinker/go-common/utils" + + strategy_filter "github.com/APIParkLab/APIPark/strategy-filter" +) + +var _ strategy_filter.IRemoteFilter = (*imlAppFilter)(nil) + +type imlAppFilter struct { + service service.IServiceService `autowired:""` +} + +func (i *imlAppFilter) Name() string { + return "application" +} + +func (i *imlAppFilter) Title() string { + return "消费者" +} + +func (i *imlAppFilter) Labels(values ...string) []string { + if len(values) == 0 { + return nil + } + if values[0] == strategy_filter.ValuesALL { + return []string{ + "全部消费者", + } + } + apps, err := i.service.AppList(context.Background(), values...) + if err != nil { + log.Error(err) + return nil + } + return utils.SliceToSlice(apps, func(a *service.Service) string { + return a.Name + }) +} + +func (i *imlAppFilter) Type() string { + return strategy_filter.TypeRemote +} + +func (i *imlAppFilter) Scopes() []string { + return []string{ + strategy_filter.ScopeGlobal, + strategy_filter.ScopeService, + } +} + +func (i *imlAppFilter) Option() *strategy_filter.Option { + return &strategy_filter.Option{ + Name: i.Name(), + Title: i.Title(), + Type: i.Type(), + } +} + +func (i *imlAppFilter) Titles() []strategy_filter.OptionTitle { + return []strategy_filter.OptionTitle{ + { + Field: "name", + Title: "consumer", + }, + { + Field: "id", + Title: "consumer id", + }, + { + Field: "description", + Title: "description", + }, + } +} + +func (i *imlAppFilter) Key() string { + return "id" +} + +func (i *imlAppFilter) Target() string { + return "list" +} + +func (i *imlAppFilter) RemoteList(ctx context.Context, keyword string, condition map[string]interface{}, page int, pageSize int) ([]any, int64, error) { + if condition == nil { + condition = make(map[string]interface{}) + } + condition["as_app"] = true + if pageSize == -1 { + // 获取全部 + list, err := i.service.Search(ctx, keyword, condition, "update_at") + if err != nil { + return nil, 0, err + } + return utils.SliceToSlice(list, func(s *service.Service) any { + return &service_dto.SimpleAppItem{ + Id: s.Id, + Name: s.Name, + Team: auto.UUID(s.Team), + Description: s.Description, + } + }), int64(len(list)), nil + } + list, total, err := i.service.SearchByPage(ctx, keyword, condition, page, pageSize, "update_at") + if err != nil { + return nil, 0, err + } + return utils.SliceToSlice(list, func(s *service.Service) any { + return &service_dto.SimpleAppItem{ + Id: s.Id, + Name: s.Name, + Team: auto.UUID(s.Team), + Description: s.Description, + } + }), total, nil +} diff --git a/module/service/iml.go b/module/service/iml.go index 64187a18..652c1a97 100644 --- a/module/service/iml.go +++ b/module/service/iml.go @@ -109,9 +109,7 @@ func (i *imlServiceModule) ExportAll(ctx context.Context) ([]*service_dto.Export 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 } @@ -191,47 +189,47 @@ func (i *imlServiceModule) SearchMyServices(ctx context.Context, teamId string, // }), nil //} -//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, -// Description: p.Description, -// Team: auto.UUID(p.Team), -// }) -// } -// return items, nil -//} -// -//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, -// Description: p.Description, -// Team: auto.UUID(p.Team), -// }) -// } -// return items, nil -//} +func (i *imlServiceModule) Simple(ctx context.Context) ([]*service_dto.SimpleServiceItem, error) { + w := make(map[string]interface{}) + w["as_server"] = true + + services, err := i.serviceService.Search(ctx, "", 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, + Description: p.Description, + Team: auto.UUID(p.Team), + }) + } + return items, nil +} + +func (i *imlServiceModule) MySimple(ctx context.Context) ([]*service_dto.SimpleServiceItem, error) { + services, err := i.searchMyServices(ctx, "", "") + + 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, + Description: p.Description, + Team: auto.UUID(p.Team), + }) + } + return items, nil +} func (i *imlServiceModule) Get(ctx context.Context, id string) (*service_dto.Service, error) { now := time.Now() diff --git a/module/service/module.go b/module/service/module.go index 31afdede..bbcc749f 100644 --- a/module/service/module.go +++ b/module/service/module.go @@ -4,6 +4,8 @@ import ( "context" "reflect" + strategy_filter "github.com/APIParkLab/APIPark/strategy-filter" + "github.com/APIParkLab/APIPark/module/system" service_dto "github.com/APIParkLab/APIPark/module/service/dto" @@ -25,11 +27,11 @@ type IServiceModule interface { // Delete 删除项目 Delete(ctx context.Context, id string) error - // Simple 获取简易项目列表 - //Simple(ctx context.Context, keyword string) ([]*service_dto.SimpleServiceItem, error) + //Simple 获取简易项目列表 + Simple(ctx context.Context) ([]*service_dto.SimpleServiceItem, error) - // MySimple 获取我的简易项目列表 - //MySimple(ctx context.Context, keyword string) ([]*service_dto.SimpleServiceItem, error) + //MySimple 获取我的简易项目列表 + MySimple(ctx context.Context) ([]*service_dto.SimpleServiceItem, error) } type IServiceDocModule interface { @@ -83,4 +85,8 @@ func init() { return reflect.ValueOf(serviceDocModule) }) + filter := new(imlAppFilter) + autowire.Autowired(filter) + strategy_filter.RegisterRemoteFilter(filter) + } diff --git a/module/strategy/driver/filter.go b/module/strategy/driver/filter.go deleted file mode 100644 index 0fdd9b6a..00000000 --- a/module/strategy/driver/filter.go +++ /dev/null @@ -1,132 +0,0 @@ -package strategy_driver - -import ( - "fmt" - "regexp" - - strategy_dto "github.com/APIParkLab/APIPark/module/strategy/dto" -) - -type FilterOptionsItem struct { - Name string - Title string - Type string - Pattern *regexp.Regexp - Options []string -} - -const ( - FilterMethod = "method" - FilterPath = "path" - FilterIP = "ip" - FilterApplication = "application" - FilterApi = "api" - FilterService = "service" - FilterAppKey = "appkey" - - FilterTypeRemote = "remote" - FilterTypePattern = "pattern" - FilterTypeStatic = "static" - - FilterValuesALL = "ALL" -) - -const ( - ApiPathRegexp = `^\*?[\w-/]+\*?$` - CIDRIpv4Exp = `^(?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([1-9]|[1-2]\d|3[0-2]))?$` -) - -const ( - HttpALL = "ALL" - HttpGET = "GET" - HttpPOST = "POST" - HttpPUT = "PUT" - HttpDELETE = "DELETE" - HttpPATCH = "PATCH" - HttpHEADER = "HEADER" - HttpOPTIONS = "OPTIONS" -) - -var ( - staticOptions = []*FilterOptionsItem{ - { - Name: FilterMethod, - Title: "API请求方式", - Type: FilterTypeStatic, - Options: []string{HttpALL, HttpGET, HttpPOST, HttpPUT, HttpDELETE, HttpPATCH, HttpHEADER, HttpOPTIONS}, - }, { - Name: FilterPath, - Title: "API路径", - Type: FilterTypePattern, - Pattern: regexp.MustCompile(ApiPathRegexp), - }, { - Name: FilterIP, - Title: "IP", - Type: FilterTypePattern, - Pattern: regexp.MustCompile(CIDRIpv4Exp), - }, - } - globalFilters = map[string]struct{}{} - serviceFilters = map[string]struct{}{} -) - -func init() { - for _, option := range staticOptions { - globalFilters[option.Name] = struct{}{} - } - for _, option := range staticOptions { - serviceFilters[option.Name] = struct{}{} - } -} - -func CheckFilters(name string, scope strategy_dto.Scope, filters []*strategy_dto.Filter) error { - var fs map[string]struct{} - switch scope.String() { - case strategy_dto.ScopeSystem: - fs = globalFilters - case strategy_dto.ScopeService: - fs = serviceFilters - default: - return fmt.Errorf("unknown scope %s", scope) - } - filterNameSet := make(map[string]struct{}) - for _, filter := range filters { - _, ok := fs[filter.Name] - if !ok { - return fmt.Errorf("%s filter %s not found", name, filter.Name) - } - if len(filter.Values) == 0 { - return fmt.Errorf("%s.Options can't be null. filter.Name:%s ", name, filter.Name) - } - - if _, has := filterNameSet[filter.Name]; has { - return fmt.Errorf("%s.Name %s is reduplicative. ", name, filter.Name) - } - filterNameSet[filter.Name] = struct{}{} - } - - return nil -} - -type OptionTitle struct { - Field string `json:"field"` - Title string `json:"title" aoi18n:""` -} - -type FilterOptionConfig struct { - Title string - Titles []OptionTitle - Key string -} - -type IFilterOptionHandler interface { - Name() string - Config() FilterOptionConfig - GetOptions(keyword, conditions map[string]interface{}, pageNum, pageSize int) ([]any, int) - Labels(values ...string) []string - Label(value string) string -} - -type IFilterFactory interface { - GetHandler(name string) IFilterOptionHandler -} diff --git a/module/strategy/dto/enum.go b/module/strategy/dto/enum.go index 9ba86462..47932b4b 100644 --- a/module/strategy/dto/enum.go +++ b/module/strategy/dto/enum.go @@ -1,7 +1,7 @@ package strategy_dto const ( - ScopeSystem = "system" + ScopeGlobal = "global" ScopeTeam = "team" ScopeService = "service" @@ -16,13 +16,13 @@ type Scope int func (s Scope) String() string { switch s { case 0: - return ScopeSystem + return ScopeGlobal case 1: return ScopeTeam case 2: return ScopeService default: - return ScopeSystem + return ScopeGlobal } } @@ -32,7 +32,7 @@ func (s Scope) Int() int { func ToScope(s string) Scope { switch s { - case ScopeSystem: + case ScopeGlobal: return 0 case ScopeTeam: return 1 diff --git a/module/strategy/dto/output.go b/module/strategy/dto/output.go index 5c4727de..e299f257 100644 --- a/module/strategy/dto/output.go +++ b/module/strategy/dto/output.go @@ -2,12 +2,13 @@ package strategy_dto import ( "encoding/json" + "time" "github.com/APIParkLab/APIPark/service/strategy" "github.com/eolinker/go-common/auto" ) -func ToStrategyItem(s *strategy.Strategy, publishVersion string) *StrategyItem { +func StrategyStatus(s *strategy.Strategy, publishVersion string) string { publishStatus := PublishStatusOffline if publishVersion != "" { if s.IsDelete { @@ -21,17 +22,36 @@ func ToStrategyItem(s *strategy.Strategy, publishVersion string) *StrategyItem { } } } + return publishStatus +} + +func ToStrategyItem(s *strategy.Strategy, publishVersion string, filters string) *StrategyItem { + publishStatus := PublishStatusOffline + if publishVersion != "" { + if s.IsDelete { + publishStatus = PublishStatusDelete + } else { + version := s.UpdateAt.Format("20060102150405") + if version != publishVersion { + publishStatus = PublishStatusUpdate + } else { + publishStatus = PublishStatusOnline + } + } + } + return &StrategyItem{ Id: s.Id, Name: s.Name, - Priority: 0, + Priority: s.Priority, Desc: s.Desc, - Filters: "", + Filters: filters, Updater: auto.UUID(s.Updater), UpdateTime: auto.TimeLabel(s.UpdateAt), ProcessedTotal: 0, PublishStatus: publishStatus, IsStop: s.IsStop, + IsDelete: s.IsDelete, } } @@ -70,4 +90,25 @@ type StrategyItem struct { ProcessedTotal int `json:"processed_total"` PublishStatus string `json:"publish_status"` IsStop bool `json:"is_stop"` + IsDelete bool `json:"is_delete"` +} + +type FilterOption struct { + Name string `json:"name"` + Title string `json:"title"` + Type string `json:"type"` + Pattern string `json:"pattern"` + Options []string `json:"options"` +} + +type Title struct { + Field string `json:"field"` + Title string `json:"title" aoi18n:""` +} + +type ToPublishItem struct { + Name string `json:"name"` + Priority int `json:"priority"` + Status string `json:"status"` + OptTime time.Time `json:"opt_time"` } diff --git a/module/strategy/iml.go b/module/strategy/iml.go index c6e7c756..8c37b631 100644 --- a/module/strategy/iml.go +++ b/module/strategy/iml.go @@ -3,7 +3,15 @@ package strategy import ( "context" "encoding/json" + "errors" "fmt" + "strings" + + "gorm.io/gorm" + + "github.com/eolinker/eosc/log" + + strategy_filter "github.com/APIParkLab/APIPark/strategy-filter" "github.com/eolinker/go-common/store" @@ -27,6 +35,34 @@ type imlStrategyModule struct { transaction store.ITransaction `autowired:""` } +func (i *imlStrategyModule) ToPublish(ctx context.Context, driver string) ([]*strategy_dto.ToPublishItem, error) { + scope := strategy_dto.ToScope(strategy_dto.ScopeGlobal) + list, err := i.strategyService.SearchAll(ctx, "", driver, scope.Int(), "") + if err != nil { + return nil, err + } + strategyIds := utils.SliceToSlice(list, func(l *strategy.Strategy) string { return l.Id }) + commits, err := i.strategyService.ListLatestStrategyCommit(ctx, scope.String(), "", strategyIds...) + if err != nil { + return nil, err + } + commitMap := utils.SliceToMapO(commits, func(c *commit.Commit[strategy.StrategyCommit]) (string, string) { return c.Key, c.Data.Version }) + items := make([]*strategy_dto.ToPublishItem, 0, len(list)) + for _, l := range list { + status := strategy_dto.StrategyStatus(l, commitMap[l.Id]) + if status == strategy_dto.PublishStatusOnline { + continue + } + items = append(items, &strategy_dto.ToPublishItem{ + Name: l.Name, + Priority: l.Priority, + Status: status, + OptTime: l.UpdateAt, + }) + } + return items, nil +} + func (i *imlStrategyModule) Search(ctx context.Context, keyword string, driver string, scope strategy_dto.Scope, target string, page int, pageSize int, filters []string, order ...string) ([]*strategy_dto.StrategyItem, int64, error) { list, total, err := i.strategyService.Search(ctx, keyword, driver, scope.Int(), target, page, pageSize, filters, order...) if err != nil { @@ -40,7 +76,19 @@ func (i *imlStrategyModule) Search(ctx context.Context, keyword string, driver s commitMap := utils.SliceToMapO(commits, func(c *commit.Commit[strategy.StrategyCommit]) (string, string) { return c.Key, c.Data.Version }) items := make([]*strategy_dto.StrategyItem, 0, len(list)) for _, l := range list { - item := strategy_dto.ToStrategyItem(l, commitMap[l.Id]) + fs := make([]*strategy_dto.Filter, 0) + + json.Unmarshal([]byte(l.Filters), &fs) + filterList := make([]string, 0, len(fs)) + for _, f := range fs { + info, err := strategy_filter.FilterLabel(f.Name, f.Values) + if err != nil { + log.Errorf("get filter label error: %v", err) + continue + } + filterList = append(filterList, fmt.Sprintf("[%s:%s]", info.Title, info.Label)) + } + item := strategy_dto.ToStrategyItem(l, commitMap[l.Id], strings.Join(filterList, ";")) items = append(items, item) } return items, total, nil @@ -51,7 +99,17 @@ func (i *imlStrategyModule) Get(ctx context.Context, id string) (*strategy_dto.S if err != nil { return nil, err } - return strategy_dto.ToStrategy(info), nil + s := strategy_dto.ToStrategy(info) + for _, f := range s.Filters { + ff, has := strategy_filter.FilterGet(f.Name) + if !has { + return nil, fmt.Errorf("filter not found: %s", f.Name) + } + f.Title = ff.Title() + f.Type = ff.Type() + f.Label = strings.Join(ff.Labels(f.Values...), ",") + } + return s, nil } func (i *imlStrategyModule) Create(ctx context.Context, input *strategy_dto.Create) error { @@ -65,7 +123,7 @@ func (i *imlStrategyModule) Create(ctx context.Context, input *strategy_dto.Crea if input.Priority < 1 { input.Priority = 1000 } - err := strategy_driver.CheckFilters(input.Driver, input.Scope, input.Filters) + err := strategy_filter.CheckFilters(input.Driver, input.Scope, input.Filters) if err != nil { return err } @@ -102,7 +160,7 @@ func (i *imlStrategyModule) Edit(ctx context.Context, id string, input *strategy } filters := info.Filters if input.Filters != nil { - err = strategy_driver.CheckFilters(info.Driver, strategy_dto.Scope(info.Scope), *input.Filters) + err = strategy_filter.CheckFilters(info.Driver, strategy_dto.Scope(info.Scope), *input.Filters) if err != nil { return err } @@ -138,8 +196,8 @@ func (i *imlStrategyModule) Disable(ctx context.Context, id string) error { return i.strategyService.Save(ctx, id, &strategy.Edit{IsStop: &stop}) } -func (i *imlStrategyModule) Publish(ctx context.Context, scope string, target string) error { - list, err := i.strategyService.AllByScope(ctx, strategy_dto.ToScope(scope).Int(), target) +func (i *imlStrategyModule) Publish(ctx context.Context, driver string, scope string, target string) error { + list, err := i.strategyService.AllByScope(ctx, driver, strategy_dto.ToScope(scope).Int(), target) if err != nil { return err } @@ -163,5 +221,12 @@ func (i *imlStrategyModule) Publish(ctx context.Context, scope string, target st } func (i *imlStrategyModule) Delete(ctx context.Context, id string) error { + _, err := i.strategyService.LatestStrategyCommit(ctx, strategy_dto.ScopeGlobal, "", id) + if err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + return nil + } + return i.strategyService.Delete(ctx, id) + } return i.strategyService.SortDelete(ctx, id) } diff --git a/module/strategy/module.go b/module/strategy/module.go index fde3e317..d35ee39a 100644 --- a/module/strategy/module.go +++ b/module/strategy/module.go @@ -17,8 +17,9 @@ type IStrategyModule interface { Edit(ctx context.Context, id string, i *strategy_dto.Edit) error Enable(ctx context.Context, id string) error Disable(ctx context.Context, id string) error - Publish(ctx context.Context, scope string, target string) error + Publish(ctx context.Context, driver string, scope string, target string) error Delete(ctx context.Context, id string) error + ToPublish(ctx context.Context, driver string) ([]*strategy_dto.ToPublishItem, error) } func init() { diff --git a/module/system/dto/input.go b/module/system/dto/input.go index 23230ba1..1b7d8f35 100644 --- a/module/system/dto/input.go +++ b/module/system/dto/input.go @@ -7,6 +7,7 @@ import ( type InputSetting struct { InvokeAddress string `json:"invoke_address" key:"system.node.invoke_address"` + SitePrefix string `json:"site_prefix" key:"system.setting.site_prefix"` } func (i *InputSetting) Validate() error { diff --git a/module/system/dto/output.go b/module/system/dto/output.go index b1d9a36d..d6ea63d3 100644 --- a/module/system/dto/output.go +++ b/module/system/dto/output.go @@ -7,6 +7,7 @@ import ( type Setting struct { InvokeAddress string `json:"invoke_address" key:"system.node.invoke_address"` + SitePrefix string `json:"site_prefix" key:"system.setting.site_prefix"` } func MapStringToStruct[T any](m map[string]string) *T { diff --git a/plugins/core/monitor.go b/plugins/core/monitor.go index 5b8a53e1..64afa039 100644 --- a/plugins/core/monitor.go +++ b/plugins/core/monitor.go @@ -17,5 +17,10 @@ func (p *plugin) monitorStatisticApis() []pm3.Api { pm3.CreateApiWidthDoc(http.MethodPost, "/api/v1/monitor/config", []string{"context", "body"}, []string{"info"}, p.monitorConfigController.SaveMonitorConfig, access.SystemSettingsDataSourceManager), pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/monitor/config", []string{"context"}, []string{"info"}, p.monitorConfigController.GetMonitorConfig, access.SystemSettingsDataSourceView), pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/simple/monitor/clusters", []string{"context"}, []string{"clusters"}, p.monitorConfigController.GetMonitorCluster), + + pm3.CreateApiWidthDoc(http.MethodPost, "/api/v1/monitor/:data_type", []string{"context", "rest:data_type", "body"}, []string{"statistics"}, p.monitorStatisticController.Statistics), + pm3.CreateApiWidthDoc(http.MethodPost, "/api/v1/monitor/:data_type/trend", []string{"context", "rest:data_type", "query:id", "body"}, []string{"tendency", "time_interval"}, p.monitorStatisticController.InvokeTrend), + pm3.CreateApiWidthDoc(http.MethodPost, "/api/v1/monitor/:data_type/trend/:typ", []string{"context", "rest:data_type", "rest:typ", "query:api", "query:provider", "query:subscriber", "body"}, []string{"tendency", "time_interval"}, p.monitorStatisticController.InvokeTrendInner), + pm3.CreateApiWidthDoc(http.MethodPost, "/api/v1/monitor/:data_type/statistics/:typ", []string{"context", "rest:data_type", "rest:typ", "query:id", "body"}, []string{"statistics"}, p.monitorStatisticController.StatisticsInner), } } diff --git a/plugins/core/service.go b/plugins/core/service.go index 774ed852..d2ff224a 100644 --- a/plugins/core/service.go +++ b/plugins/core/service.go @@ -15,6 +15,8 @@ func (p *plugin) ServiceApis() []pm3.Api { pm3.CreateApiWidthDoc(http.MethodDelete, "/api/v1/service/info", []string{"context", "query:service"}, nil, p.serviceController.Delete, access.SystemWorkspaceServiceManagerAll, access.TeamTeamServiceManager), pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/my_services", []string{"context", "query:team", "query:keyword"}, []string{"services"}, p.serviceController.SearchMyServices), pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/services", []string{"context", "query:team", "query:keyword"}, []string{"services"}, p.serviceController.Search, access.SystemWorkspaceServiceViewAll, access.TeamTeamServiceView), + pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/simple/services", []string{"context"}, []string{"services"}, p.serviceController.Simple), + pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/simple/services/mine", []string{"context"}, []string{"services"}, p.serviceController.MySimple), // 应用相关 pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/app/info", []string{"context", "query:app"}, []string{"app"}, p.appController.GetApp, access.SystemWorkspaceApplicationViewAll, access.TeamTeamConsumerView), pm3.CreateApiWidthDoc(http.MethodDelete, "/api/v1/app", []string{"context", "query:app"}, nil, p.appController.DeleteApp, access.SystemWorkspaceApplicationManagerAll, access.TeamTeamConsumerManager), diff --git a/plugins/core/strategy.go b/plugins/core/strategy.go index aa811063..64e10396 100644 --- a/plugins/core/strategy.go +++ b/plugins/core/strategy.go @@ -13,13 +13,21 @@ func (p *plugin) strategyApis() []pm3.Api { pm3.CreateApiWidthDoc(http.MethodPost, "/api/v1/strategy/global/:driver", []string{"context", "rest:driver", "body"}, nil, p.strategyController.CreateGlobalStrategy), pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/strategy/global/:driver", []string{"context", "query:strategy", "body"}, nil, p.strategyController.EditStrategy), pm3.CreateApiWidthDoc(http.MethodDelete, "/api/v1/strategy/global/:driver", []string{"context", "query:strategy"}, nil, p.strategyController.DeleteStrategy), - pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/strategy/global/:driver/enable", []string{"context", "query:strategy"}, nil, p.strategyController.EnableStrategy), - pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/strategy/global/:driver/disable", []string{"context", "query:strategy"}, nil, p.strategyController.DisableStrategy), + pm3.CreateApiWidthDoc(http.MethodPatch, "/api/v1/strategy/global/:driver/enable", []string{"context", "query:strategy"}, nil, p.strategyController.EnableStrategy), + pm3.CreateApiWidthDoc(http.MethodPatch, "/api/v1/strategy/global/:driver/disable", []string{"context", "query:strategy"}, nil, p.strategyController.DisableStrategy), + pm3.CreateApiWidthDoc(http.MethodPost, "/api/v1/strategy/global/:driver/publish", []string{"context", "rest:driver"}, nil, p.strategyController.PublishGlobalStrategy), pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/strategy/service/:driver/list", []string{"context", "query:keyword", "query:service", "rest:driver", "query:page", "query:page_size", "query:order", "query:sort", "query:filters"}, []string{"list", "total"}, p.strategyController.ServiceStrategyList), pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/strategy/service/:driver", []string{"context", "query:strategy"}, []string{"strategy"}, p.strategyController.GetStrategy), pm3.CreateApiWidthDoc(http.MethodPost, "/api/v1/strategy/service/:driver", []string{"context", "query:service", "rest:driver", "body"}, nil, p.strategyController.CreateServiceStrategy), - pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/strategy/service/:driver/enable", []string{"context", "query:strategy"}, nil, p.strategyController.EnableStrategy), - pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/strategy/service/:driver/disable", []string{"context", "query:strategy"}, nil, p.strategyController.DisableStrategy), + pm3.CreateApiWidthDoc(http.MethodPatch, "/api/v1/strategy/service/:driver/enable", []string{"context", "query:strategy"}, nil, p.strategyController.EnableStrategy), + pm3.CreateApiWidthDoc(http.MethodPatch, "/api/v1/strategy/service/:driver/disable", []string{"context", "query:strategy"}, nil, p.strategyController.DisableStrategy), + + pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/strategy/global/filter-options", []string{"context"}, []string{"options"}, p.strategyController.FilterGlobalOptions), + pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/strategy/service/filter-options", []string{"context"}, []string{"options"}, p.strategyController.FilterServiceOptions), + pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/strategy/filter-remote/:name", []string{"context", "rest:name"}, []string{"titles", "list", "total", "key", "value"}, p.strategyController.FilterGlobalRemote), + pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/strategy/service/filter-remote/:name", []string{"context", "query:service", "rest:name"}, []string{"titles", "list", "total", "key", "value"}, p.strategyController.FilterServiceRemote), + + pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/strategy/global/:driver/to-publishs", []string{"context", "rest:driver"}, []string{"strategies", "source", "version_name", "is_publish"}, p.strategyController.ToPublish), } } diff --git a/resources/locale/i18n/zh-CN.json b/resources/locale/i18n/zh-CN.json index e9b000f4..52bf043b 100644 --- a/resources/locale/i18n/zh-CN.json +++ b/resources/locale/i18n/zh-CN.json @@ -5,12 +5,18 @@ "api": "API", "api doc": "API文档", "api gateway": "API网关", + "api method": "API请求方法", + "api name": "API名称", + "api path": "API路径", + "all method": "全部请求方式", "api portal": "API门户", "authorization": "访问授权", "consumer": "消费者", + "consumer id": "消费者ID", "consumer admin": "消费者管理员", "consumer developer": "消费者开发者", "create": "创建", + "description": "描述", "data source": "数据源", "devops admin": "运维管理员", "general": "常规设置", @@ -42,5 +48,7 @@ "view all service": "查看所有服务", "view all team": "查看所有团队", "view subscribed services": "查看已经订阅的服务", - "workspace": "工作空间" + "workspace": "工作空间", + "request path": "请求路径", + "methods": "请求方法" } diff --git a/service/api/iml.go b/service/api/iml.go index 359d02db..866bb841 100644 --- a/service/api/iml.go +++ b/service/api/iml.go @@ -41,6 +41,30 @@ type imlAPIService struct { universally.IServiceDelete } +func (i *imlAPIService) ListForServices(ctx context.Context, serviceIds ...string) ([]*API, error) { + w := 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.SliceToSlice(list, FromEntity), nil +} + +func (i *imlAPIService) ListInfoForServices(ctx context.Context, serviceIds ...string) ([]*Info, error) { + w := map[string]interface{}{} + if len(serviceIds) > 0 { + w["service"] = serviceIds + } + list, err := i.apiInfoStore.List(ctx, w) + if err != nil { + return nil, err + } + return utils.SliceToSlice(list, FromEntityInfo), nil +} + func (i *imlAPIService) ListLatestCommitRequest(ctx context.Context, aid ...string) ([]*commit.Commit[Request], error) { return i.requestCommitService.ListLatest(ctx, aid...) } diff --git a/service/api/service.go b/service/api/service.go index 09911b0e..9c58c335 100644 --- a/service/api/service.go +++ b/service/api/service.go @@ -17,9 +17,11 @@ type IAPIService interface { CountMapByService(ctx context.Context, service ...string) (map[string]int64, error) Exist(ctx context.Context, aid string, api *Exist) error ListForService(ctx context.Context, serviceId string) ([]*API, error) + ListForServices(ctx context.Context, serviceIds ...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) + ListInfoForServices(ctx context.Context, serviceIds ...string) ([]*Info, error) ListLatestCommitProxy(ctx context.Context, aid ...string) ([]*commit.Commit[Proxy], error) LatestProxy(ctx context.Context, aid string) (*commit.Commit[Proxy], error) diff --git a/service/strategy/iml.go b/service/strategy/iml.go index 5831d7a9..21a433cd 100644 --- a/service/strategy/iml.go +++ b/service/strategy/iml.go @@ -22,12 +22,26 @@ type imlStrategyService struct { universally.IServiceEdit[Edit] } -func (i *imlStrategyService) AllByScope(ctx context.Context, scope int, target string) ([]*Strategy, error) { +func (i *imlStrategyService) SearchAll(ctx context.Context, keyword string, driver string, scope int, target string) ([]*Strategy, error) { w := make(map[string]interface{}) w["scope"] = scope if target != "" { w["target"] = target } + list, err := i.store.Search(ctx, keyword, w, "update_at") + if err != nil { + return nil, err + } + return utils.SliceToSlice(list, FromEntity), nil +} + +func (i *imlStrategyService) AllByScope(ctx context.Context, driver string, scope int, target string) ([]*Strategy, error) { + w := make(map[string]interface{}) + w["scope"] = scope + if target != "" { + w["target"] = target + } + w["driver"] = driver list, err := i.store.List(ctx, w) if err != nil { return nil, err @@ -152,11 +166,13 @@ func createEntityHandler(i *Create) *strategy.Strategy { Desc: i.Desc, Filters: i.Filters, Config: i.Config, + Driver: i.Driver, Scope: i.Scope, Target: i.Target, CreateAt: now, UpdateAt: now, - IsStop: true, + IsStop: false, + IsDelete: false, } } func updateHandler(e *strategy.Strategy, i *Edit) { diff --git a/service/strategy/service.go b/service/strategy/service.go index 557aeb51..7dfe0b91 100644 --- a/service/strategy/service.go +++ b/service/strategy/service.go @@ -14,8 +14,9 @@ import ( type IStrategyService interface { universally.IServiceCreate[Create] universally.IServiceEdit[Edit] - AllByScope(ctx context.Context, scope int, target string) ([]*Strategy, error) + AllByScope(ctx context.Context, driver string, scope int, target string) ([]*Strategy, error) Search(ctx context.Context, keyword string, driver string, scope int, target string, page int, pageSize int, filters []string, order ...string) ([]*Strategy, int64, error) + SearchAll(ctx context.Context, keyword string, driver string, scope int, target string) ([]*Strategy, error) Get(ctx context.Context, id string) (*Strategy, error) SortDelete(ctx context.Context, id string) error Delete(ctx context.Context, id ...string) error diff --git a/stores/strategy/model.go b/stores/strategy/model.go index 7be1799a..c7287ef6 100644 --- a/stores/strategy/model.go +++ b/stores/strategy/model.go @@ -6,7 +6,7 @@ type Strategy 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"` - Priority int `gorm:"type:tinyint(4);not null;column:priority;comment:优先级"` + Priority int `gorm:"type:int(11);not null;column:priority;comment:优先级"` Desc string `gorm:"type:text;null;column:desc;comment:描述"` Filters string `gorm:"type:mediumtext;null;column:filters;comment:筛选条件"` Config string `gorm:"type:mediumtext;null;column:config;comment:配置"` diff --git a/strategy-filter/filter.go b/strategy-filter/filter.go new file mode 100644 index 00000000..0d74cd6e --- /dev/null +++ b/strategy-filter/filter.go @@ -0,0 +1,192 @@ +package strategy_filter + +import ( + "context" + "fmt" + "regexp" + "strings" + + "github.com/eolinker/eosc" + + strategy_dto "github.com/APIParkLab/APIPark/module/strategy/dto" +) + +var ( + filterHandler = NewHandler() +) + +type Option struct { + Name string + Title string + Type string + Pattern *regexp.Regexp + Options []string +} + +const ( + TypeRemote = "remote" + TypePattern = "pattern" + TypeStatic = "static" + + ValuesALL = "ALL" + + ScopeGlobal = "global" + ScopeTeam = "team" + ScopeService = "service" +) + +const ( + HttpALL = "ALL" +) + +func CheckFilters(name string, scope strategy_dto.Scope, filters []*strategy_dto.Filter) error { + fs, ok := filterHandler.Options(scope.String()) + if !ok { + return fmt.Errorf("unknown scope %s", scope) + } + filterNameSet := make(map[string]struct{}) + for _, filter := range filters { + op, ok := fs[filter.Name] + if !ok { + return fmt.Errorf("%s filter %s not found", name, filter.Name) + } + for _, value := range filter.Values { + if op.Pattern != nil && !op.Pattern.MatchString(value) { + return fmt.Errorf("%s filter %s value %s not match pattern", name, filter.Name, value) + } + } + + if len(filter.Values) == 0 { + return fmt.Errorf("%s.Options can't be null. filter.Name:%s ", name, filter.Name) + } + + if _, has := filterNameSet[filter.Name]; has { + return fmt.Errorf("%s.Name %s is reduplicative. ", name, filter.Name) + } + filterNameSet[filter.Name] = struct{}{} + } + + return nil +} + +type OptionTitle struct { + Field string `json:"field"` + Title string `json:"title" aoi18n:""` +} + +type IRemoteFilter interface { + IFilter + Titles() []OptionTitle + Key() string + Target() string + RemoteList(ctx context.Context, keyword string, condition map[string]interface{}, page int, pageSize int) ([]any, int64, error) +} + +type IFilter interface { + Name() string + Title() string + Labels(values ...string) []string + Type() string + Scopes() []string + Option() *Option +} + +type IFilterHandler interface { + RemoteFilter(name string) (IRemoteFilter, bool) + Options(scope string) (map[string]*Option, bool) + FilterLabel(name string, values []string) (*Info, error) +} + +type Info struct { + Title string + Label string +} + +type ScopeFilterOption eosc.Untyped[string, *Option] + +type Handler struct { + filters eosc.Untyped[string, IFilter] + remoteFilters eosc.Untyped[string, IRemoteFilter] + options eosc.Untyped[string, ScopeFilterOption] +} + +func NewHandler() *Handler { + return &Handler{ + filters: eosc.BuildUntyped[string, IFilter](), + remoteFilters: eosc.BuildUntyped[string, IRemoteFilter](), + options: eosc.BuildUntyped[string, ScopeFilterOption](), + } +} + +func (f *Handler) RemoteFilter(name string) (IRemoteFilter, bool) { + return f.remoteFilters.Get(name) +} + +func (f *Handler) RegisterRemoteFilter(filter IRemoteFilter) { + f.remoteFilters.Set(filter.Name(), filter) + f.setScopes(filter) +} + +func (f *Handler) RegisterFilter(filter IFilter) { + f.filters.Set(filter.Name(), filter) + f.setScopes(filter) +} + +func (f *Handler) setScopes(filter IFilter) { + for _, scope := range filter.Scopes() { + tmp, has := f.options.Get(scope) + if !has { + tmp = eosc.BuildUntyped[string, *Option]() + f.options.Set(scope, tmp) + } + tmp.Set(filter.Name(), filter.Option()) + } +} + +func (f *Handler) Options(scope string) (map[string]*Option, bool) { + options, has := f.options.Get(scope) + if !has { + return nil, false + } + return options.All(), true +} + +func (f *Handler) FilterLabel(name string, values []string) (*Info, error) { + if tmp, has := f.remoteFilters.Get(name); has { + return &Info{ + Title: tmp.Title(), + Label: strings.Join(tmp.Labels(values...), ","), + }, nil + } + if tmp, has := f.filters.Get(name); has { + return &Info{ + Title: tmp.Title(), + Label: strings.Join(tmp.Labels(values...), ","), + }, nil + } + return nil, fmt.Errorf("filter %s not found", name) +} + +func RegisterRemoteFilter(filter IRemoteFilter) { + filterHandler.RegisterRemoteFilter(filter) +} + +func RemoteFilter(name string) (IRemoteFilter, bool) { + return filterHandler.RemoteFilter(name) +} + +func Options(scope string) (map[string]*Option, bool) { + return filterHandler.Options(scope) +} + +func FilterLabel(name string, values []string) (*Info, error) { + return filterHandler.FilterLabel(name, values) +} + +func FilterGet(name string) (IFilter, bool) { + f, has := filterHandler.remoteFilters.Get(name) + if has { + return f, true + } + return filterHandler.filters.Get(name) +} diff --git a/strategy-filter/ip.go b/strategy-filter/ip.go new file mode 100644 index 00000000..7e1cc012 --- /dev/null +++ b/strategy-filter/ip.go @@ -0,0 +1,58 @@ +package strategy_filter + +import "regexp" + +var ( + CIDRIpv4Exp = `^(?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([1-9]|[1-2]\d|3[0-2]))?$` +) + +var _ IFilter = &ipFilter{} + +func init() { + filterHandler.RegisterFilter(newIpFilter()) +} + +type ipFilter struct { + name string + title string + typ string + pattern *regexp.Regexp +} + +func newIpFilter() *ipFilter { + return &ipFilter{ + name: "ip", + title: "IP", + typ: TypePattern, + pattern: regexp.MustCompile(CIDRIpv4Exp), + } +} + +func (i *ipFilter) Name() string { + return i.name +} + +func (i *ipFilter) Title() string { + return i.title +} + +func (i *ipFilter) Labels(values ...string) []string { + return values +} + +func (i *ipFilter) Type() string { + return i.typ +} + +func (i *ipFilter) Scopes() []string { + return []string{ScopeGlobal, ScopeService} +} + +func (i *ipFilter) Option() *Option { + return &Option{ + Name: i.name, + Title: i.title, + Type: i.typ, + Pattern: i.pattern, + } +} diff --git a/strategy-filter/method.go b/strategy-filter/method.go new file mode 100644 index 00000000..f91156da --- /dev/null +++ b/strategy-filter/method.go @@ -0,0 +1,60 @@ +package strategy_filter + +import "net/http" + +var _ IFilter = &methodFilter{} + +func init() { + filterHandler.RegisterFilter(newMethodFilter()) +} + +type methodFilter struct { + name string + title string + typ string + options []string +} + +func newMethodFilter() *methodFilter { + return &methodFilter{ + name: "method", + title: "api method", + typ: TypeStatic, + options: []string{HttpALL, http.MethodGet, http.MethodPost, http.MethodPut, http.MethodDelete, http.MethodPatch, http.MethodHead, http.MethodOptions}, + } +} + +func (m *methodFilter) Name() string { + return m.name +} + +func (m *methodFilter) Title() string { + return m.title +} + +func (m *methodFilter) Labels(values ...string) []string { + if len(values) > 0 && values[0] != ValuesALL { + return []string{"all method"} + } + if len(values) == 0 { + return []string{"-"} + } + return values +} + +func (m *methodFilter) Type() string { + return m.typ +} + +func (m *methodFilter) Scopes() []string { + return []string{ScopeGlobal, ScopeService} +} + +func (m *methodFilter) Option() *Option { + return &Option{ + Name: m.name, + Title: m.title, + Type: m.typ, + Options: m.options, + } +} diff --git a/strategy-filter/path.go b/strategy-filter/path.go new file mode 100644 index 00000000..3f189f05 --- /dev/null +++ b/strategy-filter/path.go @@ -0,0 +1,58 @@ +package strategy_filter + +import "regexp" + +var _ IFilter = &pathFilter{} + +var ( + ApiPathRegexp = `^\*?[\w-/]+\*?$` +) + +type pathFilter struct { + name string + title string + typ string + pattern *regexp.Regexp +} + +func init() { + filterHandler.RegisterFilter(newPathFilter()) +} + +func newPathFilter() *pathFilter { + return &pathFilter{ + name: "path", + title: "api path", + typ: TypePattern, + pattern: regexp.MustCompile(ApiPathRegexp), + } +} + +func (p *pathFilter) Name() string { + return p.name +} + +func (p *pathFilter) Title() string { + return p.title +} + +func (p *pathFilter) Labels(values ...string) []string { + return values +} + +func (p *pathFilter) Type() string { + return p.typ +} + +func (p *pathFilter) Scopes() []string { + return []string{ScopeGlobal, ScopeService} +} + +func (p *pathFilter) Option() *Option { + return &Option{ + Name: p.name, + Title: p.title, + Type: p.typ, + Pattern: p.pattern, + } +} From 601663b658f1c74df6c4a598ba0f6fef8d3a74a7 Mon Sep 17 00:00:00 2001 From: Liujian <824010343@qq.com> Date: Tue, 26 Nov 2024 23:49:44 +0800 Subject: [PATCH 04/14] system setting add site_prefix --- module/catalogue/dto/output.go | 1 + module/catalogue/iml.go | 2 ++ service/setting/iml.go | 1 + 3 files changed, 4 insertions(+) diff --git a/module/catalogue/dto/output.go b/module/catalogue/dto/output.go index 5ff509df..ef84220b 100644 --- a/module/catalogue/dto/output.go +++ b/module/catalogue/dto/output.go @@ -41,6 +41,7 @@ type ServiceBasic struct { ApprovalType string `json:"approval_type"` ServiceKind string `json:"service_kind"` InvokeAddress string `json:"invoke_address"` + SitePrefix string `json:"site_prefix"` } type ServiceApiBasic struct { diff --git a/module/catalogue/iml.go b/module/catalogue/iml.go index 4b1af781..18b58def 100644 --- a/module/catalogue/iml.go +++ b/module/catalogue/iml.go @@ -284,6 +284,7 @@ func (i *imlCatalogueModule) ServiceDetail(ctx context.Context, sid string) (*ca serviceDoc = commit.Data.Content } invokeAddress, _ := i.settingService.Get(ctx, setting.KeyInvokeAddress) + sitePrefix, _ := i.settingService.Get(ctx, setting.KeySitePrefix) return &catalogue_dto.ServiceDetail{ Name: s.Name, @@ -301,6 +302,7 @@ func (i *imlCatalogueModule) ServiceDetail(ctx context.Context, sid string) (*ca ApprovalType: s.ApprovalType.String(), ServiceKind: s.Kind.String(), InvokeAddress: invokeAddress, + SitePrefix: sitePrefix, }, APIDoc: apiDoc, }, nil diff --git a/service/setting/iml.go b/service/setting/iml.go index 99a4d829..6a81c5be 100644 --- a/service/setting/iml.go +++ b/service/setting/iml.go @@ -14,6 +14,7 @@ var ( const ( KeyInvokeAddress = "system.node.invoke_address" + KeySitePrefix = "system.setting.site_prefix" ) type imlSettingService struct { From b5671d14a4cf76c38de3a41783d0e11478d79672 Mon Sep 17 00:00:00 2001 From: Liujian <824010343@qq.com> Date: Wed, 27 Nov 2024 00:34:24 +0800 Subject: [PATCH 05/14] add api: /simple/service/apis --- controller/router/iml.go | 7 ++++++- controller/router/router.go | 4 +++- module/router/dto/input.go | 4 ++++ module/router/dto/output.go | 4 ++-- module/router/iml.go | 22 ++++++++++++++++++++++ module/router/router.go | 1 + plugins/core/api.go | 1 + 7 files changed, 39 insertions(+), 4 deletions(-) diff --git a/controller/router/iml.go b/controller/router/iml.go index d4aa2023..4e698924 100644 --- a/controller/router/iml.go +++ b/controller/router/iml.go @@ -1,12 +1,13 @@ package router import ( + "io" + 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) @@ -15,6 +16,10 @@ type imlAPIController struct { module router.IRouterModule `autowired:""` } +func (i *imlAPIController) Simple(ctx *gin.Context, input *router_dto.InputSimpleAPI) ([]*router_dto.SimpleItem, error) { + return i.module.SimpleAPIs(ctx, input) +} + func (i *imlAPIController) Detail(ctx *gin.Context, serviceId string, apiId string) (*router_dto.Detail, error) { return i.module.Detail(ctx, serviceId, apiId) } diff --git a/controller/router/router.go b/controller/router/router.go index a406947e..f682ba32 100644 --- a/controller/router/router.go +++ b/controller/router/router.go @@ -1,9 +1,10 @@ package router import ( - api_doc_dto "github.com/APIParkLab/APIPark/module/api-doc/dto" "reflect" + api_doc_dto "github.com/APIParkLab/APIPark/module/api-doc/dto" + "github.com/gin-gonic/gin" "github.com/eolinker/go-common/autowire" @@ -24,6 +25,7 @@ type IRouterController interface { Delete(ctx *gin.Context, serviceId string, apiId string) error // Prefix 获取API前缀 Prefix(ctx *gin.Context, serviceId string) (string, bool, error) + Simple(ctx *gin.Context, input *router_dto.InputSimpleAPI) ([]*router_dto.SimpleItem, error) } type IAPIDocController interface { diff --git a/module/router/dto/input.go b/module/router/dto/input.go index aee44807..02fcbf10 100644 --- a/module/router/dto/input.go +++ b/module/router/dto/input.go @@ -102,3 +102,7 @@ type ListInput struct { type UpdateDoc struct { Content string `json:"content"` } + +type InputSimpleAPI struct { + Services []string `json:"services"` +} diff --git a/module/router/dto/output.go b/module/router/dto/output.go index 45fe4888..9f8276ed 100644 --- a/module/router/dto/output.go +++ b/module/router/dto/output.go @@ -26,8 +26,8 @@ type Item struct { type SimpleItem struct { Id string `json:"id"` Methods []string `json:"methods"` - //Name string `json:"name"` - Path string `json:"request_path"` + Name string `json:"name"` + Path string `json:"request_path"` } type Detail struct { diff --git a/module/router/iml.go b/module/router/iml.go index 2be55575..e810c0e5 100644 --- a/module/router/iml.go +++ b/module/router/iml.go @@ -46,6 +46,28 @@ type imlRouterModule struct { transaction store.ITransaction `autowired:""` } +func (i *imlRouterModule) SimpleAPIs(ctx context.Context, input *router_dto.InputSimpleAPI) ([]*router_dto.SimpleItem, error) { + list, err := i.apiService.ListForServices(ctx, input.Services...) + 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 + } + + return utils.SliceToSlice(apiInfos, func(item *api.Info) *router_dto.SimpleItem { + return &router_dto.SimpleItem{ + Id: item.UUID, + Name: item.Name, + Methods: item.Methods, + Path: item.Path, + } + }), nil +} + func (i *imlRouterModule) ExportAll(ctx context.Context) ([]*router_dto.Export, error) { apiList, err := i.apiService.ListInfo(ctx) diff --git a/module/router/router.go b/module/router/router.go index 36c83273..ae433518 100644 --- a/module/router/router.go +++ b/module/router/router.go @@ -31,6 +31,7 @@ type IRouterModule interface { // Prefix 获取API前缀 Prefix(ctx context.Context, serviceId string) (string, error) + SimpleAPIs(ctx context.Context, input *router_dto.InputSimpleAPI) ([]*router_dto.SimpleItem, error) //ExportAll(ctx context.Context) ([]*router_dto.Export, error) } diff --git a/plugins/core/api.go b/plugins/core/api.go index 666cf769..62def55b 100644 --- a/plugins/core/api.go +++ b/plugins/core/api.go @@ -9,6 +9,7 @@ import ( func (p *plugin) apiApis() []pm3.Api { return []pm3.Api{ + pm3.CreateApiWidthDoc(http.MethodPost, "/api/v1/simple/service/apis", []string{"context", "body"}, []string{"apis"}, p.routerController.Simple), pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/service/routers", []string{"context", "query:keyword", "query:service"}, []string{"routers"}, p.routerController.Search, access.SystemWorkspaceServiceViewAll, access.TeamServiceApiView), pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/service/router/detail", []string{"context", "query:service", "query:router"}, []string{"router"}, p.routerController.Detail, access.SystemWorkspaceServiceViewAll, access.TeamServiceApiView), pm3.CreateApiWidthDoc(http.MethodPost, "/api/v1/service/router", []string{"context", "query:service", "body"}, []string{"router"}, p.routerController.Create, access.SystemWorkspaceServiceManagerAll, access.TeamServiceApiManager), From 4e902e891e9e285b589817034d3b0ce34c00492c Mon Sep 17 00:00:00 2001 From: Liujian <824010343@qq.com> Date: Wed, 27 Nov 2024 11:46:56 +0800 Subject: [PATCH 06/14] add strategy permit --- controller/strategy/iml.go | 8 ++++++++ controller/strategy/strategy.go | 3 +++ module/strategy/iml.go | 23 +++++++++++++++++++++-- module/strategy/module.go | 3 +++ plugins/core/strategy.go | 3 +++ resources/access/access.yaml | 20 ++++++++++++++++++++ resources/access/role.yaml | 10 +++++++++- service/strategy/iml.go | 5 +++++ service/strategy/service.go | 2 ++ 9 files changed, 74 insertions(+), 3 deletions(-) diff --git a/controller/strategy/iml.go b/controller/strategy/iml.go index 94786b07..7f0b3e22 100644 --- a/controller/strategy/iml.go +++ b/controller/strategy/iml.go @@ -25,6 +25,14 @@ type imlStrategyController struct { serviceModule service.IServiceModule `autowired:""` } +func (i *imlStrategyController) Restore(ctx *gin.Context, id string) error { + return i.strategyModule.Restore(ctx, id) +} + +func (i *imlStrategyController) DeleteServiceStrategy(ctx *gin.Context, serviceId string, id string) error { + return i.strategyModule.DeleteServiceStrategy(ctx, serviceId, id) +} + func (i *imlStrategyController) ToPublish(ctx *gin.Context, driver string) ([]*strategy_dto.ToPublishItem, string, string, bool, error) { list, err := i.strategyModule.ToPublish(ctx, driver) if err != nil { diff --git a/controller/strategy/strategy.go b/controller/strategy/strategy.go index b6eac85a..ad0f4fac 100644 --- a/controller/strategy/strategy.go +++ b/controller/strategy/strategy.go @@ -22,6 +22,9 @@ type IStrategyController interface { DisableStrategy(ctx *gin.Context, id string) error DeleteStrategy(ctx *gin.Context, id string) error + DeleteServiceStrategy(ctx *gin.Context, serviceId string, id string) error + + Restore(ctx *gin.Context, id string) error FilterGlobalOptions(ctx *gin.Context) ([]*strategy_dto.FilterOption, error) FilterServiceOptions(ctx *gin.Context) ([]*strategy_dto.FilterOption, error) diff --git a/module/strategy/iml.go b/module/strategy/iml.go index 8c37b631..1c3032b4 100644 --- a/module/strategy/iml.go +++ b/module/strategy/iml.go @@ -35,6 +35,22 @@ type imlStrategyModule struct { transaction store.ITransaction `autowired:""` } +func (i *imlStrategyModule) Restore(ctx context.Context, id string) error { + return i.strategyService.Restore(ctx, id) +} + +func (i *imlStrategyModule) DeleteServiceStrategy(ctx context.Context, serviceId string, id string) error { + _, err := i.strategyService.LatestStrategyCommit(ctx, strategy_dto.ScopeService, serviceId, id) + if err != nil { + // 判断是否已经发布,如果未发布则直接删除 + if !errors.Is(err, gorm.ErrRecordNotFound) { + return err + } + return i.strategyService.Delete(ctx, id) + } + return i.strategyService.SortDelete(ctx, id) +} + func (i *imlStrategyModule) ToPublish(ctx context.Context, driver string) ([]*strategy_dto.ToPublishItem, error) { scope := strategy_dto.ToScope(strategy_dto.ScopeGlobal) list, err := i.strategyService.SearchAll(ctx, "", driver, scope.Int(), "") @@ -46,7 +62,7 @@ func (i *imlStrategyModule) ToPublish(ctx context.Context, driver string) ([]*st if err != nil { return nil, err } - commitMap := utils.SliceToMapO(commits, func(c *commit.Commit[strategy.StrategyCommit]) (string, string) { return c.Key, c.Data.Version }) + commitMap := utils.SliceToMapO(commits, func(c *commit.Commit[strategy.StrategyCommit]) (string, string) { return c.Data.Id, c.Data.Version }) items := make([]*strategy_dto.ToPublishItem, 0, len(list)) for _, l := range list { status := strategy_dto.StrategyStatus(l, commitMap[l.Id]) @@ -73,7 +89,7 @@ func (i *imlStrategyModule) Search(ctx context.Context, keyword string, driver s if err != nil { return nil, 0, err } - commitMap := utils.SliceToMapO(commits, func(c *commit.Commit[strategy.StrategyCommit]) (string, string) { return c.Key, c.Data.Version }) + commitMap := utils.SliceToMapO(commits, func(c *commit.Commit[strategy.StrategyCommit]) (string, string) { return c.Data.Id, c.Data.Version }) items := make([]*strategy_dto.StrategyItem, 0, len(list)) for _, l := range list { fs := make([]*strategy_dto.Filter, 0) @@ -210,6 +226,9 @@ func (i *imlStrategyModule) Publish(ctx context.Context, driver string, scope st } } + if l.IsStop { + continue + } // TODO:同步到网关 err = i.strategyService.CommitStrategy(txCtx, scope, target, l.Id, l) if err != nil { diff --git a/module/strategy/module.go b/module/strategy/module.go index d35ee39a..396622f2 100644 --- a/module/strategy/module.go +++ b/module/strategy/module.go @@ -20,6 +20,9 @@ type IStrategyModule interface { Publish(ctx context.Context, driver string, scope string, target string) error Delete(ctx context.Context, id string) error ToPublish(ctx context.Context, driver string) ([]*strategy_dto.ToPublishItem, error) + Restore(ctx context.Context, id string) error + + DeleteServiceStrategy(ctx context.Context, serviceId string, id string) error } func init() { diff --git a/plugins/core/strategy.go b/plugins/core/strategy.go index 64e10396..2ed88374 100644 --- a/plugins/core/strategy.go +++ b/plugins/core/strategy.go @@ -16,12 +16,15 @@ func (p *plugin) strategyApis() []pm3.Api { pm3.CreateApiWidthDoc(http.MethodPatch, "/api/v1/strategy/global/:driver/enable", []string{"context", "query:strategy"}, nil, p.strategyController.EnableStrategy), pm3.CreateApiWidthDoc(http.MethodPatch, "/api/v1/strategy/global/:driver/disable", []string{"context", "query:strategy"}, nil, p.strategyController.DisableStrategy), pm3.CreateApiWidthDoc(http.MethodPost, "/api/v1/strategy/global/:driver/publish", []string{"context", "rest:driver"}, nil, p.strategyController.PublishGlobalStrategy), + pm3.CreateApiWidthDoc(http.MethodPatch, "/api/v1/strategy/global/:driver/restore", []string{"context", "query:strategy"}, nil, p.strategyController.Restore), pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/strategy/service/:driver/list", []string{"context", "query:keyword", "query:service", "rest:driver", "query:page", "query:page_size", "query:order", "query:sort", "query:filters"}, []string{"list", "total"}, p.strategyController.ServiceStrategyList), pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/strategy/service/:driver", []string{"context", "query:strategy"}, []string{"strategy"}, p.strategyController.GetStrategy), pm3.CreateApiWidthDoc(http.MethodPost, "/api/v1/strategy/service/:driver", []string{"context", "query:service", "rest:driver", "body"}, nil, p.strategyController.CreateServiceStrategy), pm3.CreateApiWidthDoc(http.MethodPatch, "/api/v1/strategy/service/:driver/enable", []string{"context", "query:strategy"}, nil, p.strategyController.EnableStrategy), pm3.CreateApiWidthDoc(http.MethodPatch, "/api/v1/strategy/service/:driver/disable", []string{"context", "query:strategy"}, nil, p.strategyController.DisableStrategy), + pm3.CreateApiWidthDoc(http.MethodDelete, "/api/v1/strategy/service/:driver", []string{"context", "query:service", "query:strategy"}, nil, p.strategyController.DeleteServiceStrategy), + pm3.CreateApiWidthDoc(http.MethodPatch, "/api/v1/strategy/service/:driver/restore", []string{"context", "query:strategy"}, nil, p.strategyController.Restore), pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/strategy/global/filter-options", []string{"context"}, []string{"options"}, p.strategyController.FilterGlobalOptions), pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/strategy/service/filter-options", []string{"context"}, []string{"options"}, p.strategyController.FilterServiceOptions), diff --git a/resources/access/access.yaml b/resources/access/access.yaml index 69a6daee..6b24896f 100644 --- a/resources/access/access.yaml +++ b/resources/access/access.yaml @@ -131,6 +131,16 @@ system: value: 'manager' dependents: - system.settings.log_configuration.view + - name: global strategy + value: "strategy" + children: + - name: view + value: 'view' + guest_allow: true + - name: manager + value: 'manager' + dependents: + - system.settings.strategy.view team: - name: service value: 'service' @@ -197,6 +207,16 @@ team: value: 'manager' dependents: - team.service.subscription.view + - name: service strategy + value: 'strategy' + children: + - name: view + value: 'view' + guest_allow: true + - name: manager + value: 'manager' + dependents: + - team.service.strategy.view - name: consumer value: 'consumer' children: diff --git a/resources/access/role.yaml b/resources/access/role.yaml index 8dcd8e1a..9b9613db 100644 --- a/resources/access/role.yaml +++ b/resources/access/role.yaml @@ -19,6 +19,8 @@ system: - system.settings.role.view - system.settings.ssl_certificate.manager - system.settings.ssl_certificate.view + - system.settings.strategy.view + - system.settings.strategy.manager - system.workspace.application.manager_all - system.workspace.application.view_all - system.workspace.service.manager_all @@ -49,6 +51,8 @@ system: - system.settings.log_configuration.view - system.settings.ssl_certificate.manager - system.settings.ssl_certificate.view + - system.settings.strategy.view + - system.settings.strategy.manager - system.workspace.application.view_all - system.workspace.service.view_all - system.workspace.team.view_all @@ -75,6 +79,8 @@ team: - team.service.release.view - team.service.service_intro.manager - team.service.service_intro.view + - team.service.strategy.view + - team.service.strategy.manager - team.service.subscription.manager - team.service.subscription.view - team.service.upstream.manager @@ -99,6 +105,8 @@ team: - team.service.release.view - team.service.service_intro.manager - team.service.service_intro.view + - team.service.strategy.view + - team.service.strategy.manager - team.service.subscription.manager - team.service.subscription.view - team.service.upstream.manager @@ -120,7 +128,7 @@ team: - team.service.service.manager - team.service.service_intro.manager - team.service.service_intro.view - - team.service.subscription.manager + - team.service.strategy.view - team.service.subscription.view - team.service.upstream.manager - team.service.upstream.view diff --git a/service/strategy/iml.go b/service/strategy/iml.go index 21a433cd..ca72ac61 100644 --- a/service/strategy/iml.go +++ b/service/strategy/iml.go @@ -22,6 +22,11 @@ type imlStrategyService struct { universally.IServiceEdit[Edit] } +func (i *imlStrategyService) Restore(ctx context.Context, id string) error { + _, err := i.store.UpdateWhere(ctx, map[string]interface{}{"uuid": id}, map[string]interface{}{"is_delete": false}) + return err +} + func (i *imlStrategyService) SearchAll(ctx context.Context, keyword string, driver string, scope int, target string) ([]*Strategy, error) { w := make(map[string]interface{}) w["scope"] = scope diff --git a/service/strategy/service.go b/service/strategy/service.go index 7dfe0b91..817b2732 100644 --- a/service/strategy/service.go +++ b/service/strategy/service.go @@ -21,6 +21,8 @@ type IStrategyService interface { SortDelete(ctx context.Context, id string) error Delete(ctx context.Context, id ...string) error + Restore(ctx context.Context, id string) error + CommitStrategy(ctx context.Context, scope string, target string, strategyId string, data *Strategy) error GetStrategyCommit(ctx context.Context, commitId string) (*commit.Commit[StrategyCommit], error) LatestStrategyCommit(ctx context.Context, scope string, target string, strategyId string) (*commit.Commit[StrategyCommit], error) From 0a828e843d0bbe52555c4a2fe00799cccb0e9d12 Mon Sep 17 00:00:00 2001 From: Liujian <824010343@qq.com> Date: Wed, 27 Nov 2024 13:51:55 +0800 Subject: [PATCH 07/14] add open api swagger --- controller/service/iml.go | 61 +++++++++++++++++++++++++++++++++++ controller/service/service.go | 2 ++ controller/strategy/iml.go | 2 +- plugins/core/service.go | 1 + 4 files changed, 65 insertions(+), 1 deletion(-) diff --git a/controller/service/iml.go b/controller/service/iml.go index 1a5e024f..c8e58239 100644 --- a/controller/service/iml.go +++ b/controller/service/iml.go @@ -7,6 +7,14 @@ import ( "strings" "time" + "github.com/eolinker/go-common/pm3" + + "github.com/APIParkLab/APIPark/module/system" + + "github.com/getkin/kin-openapi/openapi3" + + api_doc "github.com/APIParkLab/APIPark/module/api-doc" + upstream_dto "github.com/APIParkLab/APIPark/module/upstream/dto" "github.com/eolinker/eosc/log" @@ -43,11 +51,64 @@ type imlServiceController struct { docModule service.IServiceDocModule `autowired:""` aiAPIModule ai_api.IAPIModule `autowired:""` routerModule router.IRouterModule `autowired:""` + apiDocModule api_doc.IAPIDocModule `autowired:""` providerModule ai.IProviderModule `autowired:""` upstreamModule upstream.IUpstreamModule `autowired:""` + settingModule system.ISettingModule `autowired:""` transaction store.ITransaction `autowired:""` } +var ( + loader = openapi3.NewLoader() +) + +func (i *imlServiceController) Swagger(ctx *gin.Context) { + id, has := ctx.Params.Get("id") + if !has { + ctx.JSON(200, &pm3.Response{ + Code: -1, + Success: "fail", + Message: fmt.Sprintf("id is required"), + }) + return + + } + doc, err := i.apiDocModule.GetDoc(ctx, id) + if err != nil { + ctx.JSON(200, &pm3.Response{ + Code: -1, + Success: "fail", + Message: err.Error(), + }) + return + } + tmp, err := loader.LoadFromData([]byte(doc.Content)) + if err != nil { + ctx.JSON(200, &pm3.Response{ + Code: -1, + Success: "fail", + Message: err.Error(), + }) + return + } + cfg := i.settingModule.Get(ctx) + + tmp.AddServer(&openapi3.Server{ + URL: cfg.InvokeAddress, + }) + data, err := tmp.MarshalJSON() + if err != nil { + ctx.JSON(200, &pm3.Response{ + Code: -1, + Success: "fail", + Message: err.Error(), + }) + return + } + ctx.JSON(200, string(data)) + return +} + func (i *imlServiceController) Simple(ctx *gin.Context) ([]*service_dto.SimpleServiceItem, error) { return i.module.Simple(ctx) } diff --git a/controller/service/service.go b/controller/service/service.go index 6f631781..ddf657f5 100644 --- a/controller/service/service.go +++ b/controller/service/service.go @@ -26,6 +26,8 @@ type IServiceController interface { SaveServiceDoc(ctx *gin.Context, id string, input *service_dto.SaveServiceDoc) error Simple(ctx *gin.Context) ([]*service_dto.SimpleServiceItem, error) MySimple(ctx *gin.Context) ([]*service_dto.SimpleServiceItem, error) + + Swagger(ctx *gin.Context) } type IAppController interface { diff --git a/controller/strategy/iml.go b/controller/strategy/iml.go index 7f0b3e22..6c35e2da 100644 --- a/controller/strategy/iml.go +++ b/controller/strategy/iml.go @@ -197,7 +197,7 @@ func (i *imlStrategyController) CreateServiceStrategy(ctx *gin.Context, serviceI } func (i *imlStrategyController) EditStrategy(ctx *gin.Context, id string, input *strategy_dto.Edit) error { - return i.EditStrategy(ctx, id, input) + return i.strategyModule.Edit(ctx, id, input) } func (i *imlStrategyController) EnableStrategy(ctx *gin.Context, id string) error { diff --git a/plugins/core/service.go b/plugins/core/service.go index d2ff224a..11a8ee5d 100644 --- a/plugins/core/service.go +++ b/plugins/core/service.go @@ -29,5 +29,6 @@ func (p *plugin) ServiceApis() []pm3.Api { pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/service/doc", []string{"context", "query:service"}, []string{"doc"}, p.serviceController.ServiceDoc, access.SystemWorkspaceServiceViewAll, access.TeamServiceServiceIntroView), pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/service/doc", []string{"context", "query:service", "body"}, nil, p.serviceController.SaveServiceDoc, access.SystemWorkspaceServiceManagerAll, access.TeamServiceServiceIntroManager), + pm3.CreateApiSimple(http.MethodGet, "/swagger/:id", p.serviceController.Swagger), } } From 3fde0fa7a3d2c25114b037d3cf0f0967c654af19 Mon Sep 17 00:00:00 2001 From: Liujian <824010343@qq.com> Date: Wed, 27 Nov 2024 19:05:11 +0800 Subject: [PATCH 08/14] update service publish rule --- controller/cluster/iml.go | 4 +- controller/service/iml.go | 84 +++++++++++++++++++++++----------- controller/service/service.go | 1 + module/cluster/impl.go | 12 ++--- module/service-diff/iml.go | 86 +++++++++++++++++++++++++++++++++++ module/service-diff/out.go | 14 +++++- module/service/iml.go | 2 +- module/strategy/iml.go | 6 +-- plugins/core/service.go | 3 +- service/service_diff/diff.go | 17 +++++-- service/strategy/iml.go | 19 ++++++-- service/strategy/service.go | 7 +-- 12 files changed, 204 insertions(+), 51 deletions(-) diff --git a/controller/cluster/iml.go b/controller/cluster/iml.go index d67de3d0..509d4af3 100644 --- a/controller/cluster/iml.go +++ b/controller/cluster/iml.go @@ -45,8 +45,8 @@ func (p *imlCluster) Check(ctx *gin.Context, input *cluster_dto.CheckCluster) ([ // return id, nil //} // -//func (p *imlCluster) Search(ctx *gin.Context, keyword string) ([]*parition_dto.Item, error) { -// return p.module.Search(ctx, keyword) +//func (p *imlCluster) SearchByDriver(ctx *gin.Context, keyword string) ([]*parition_dto.Item, error) { +// return p.module.SearchByDriver(ctx, keyword) //} // //func (p *imlCluster) Simple(ctx *gin.Context) ([]*parition_dto.Simple, error) { diff --git a/controller/service/iml.go b/controller/service/iml.go index c8e58239..e95a8238 100644 --- a/controller/service/iml.go +++ b/controller/service/iml.go @@ -62,6 +62,62 @@ var ( loader = openapi3.NewLoader() ) +func (i *imlServiceController) swagger(ctx *gin.Context, id string) (*openapi3.T, error) { + doc, err := i.apiDocModule.GetDoc(ctx, id) + if err != nil { + return nil, err + } + tmp, err := loader.LoadFromData([]byte(doc.Content)) + if err != nil { + return nil, err + } + cfg := i.settingModule.Get(ctx) + + tmp.AddServer(&openapi3.Server{ + URL: cfg.InvokeAddress, + }) + return tmp, nil +} + +func (i *imlServiceController) ExportSwagger(ctx *gin.Context) { + id, has := ctx.Params.Get("id") + if !has { + ctx.JSON(200, &pm3.Response{ + Code: -1, + Success: "fail", + Message: fmt.Sprintf("id is required"), + }) + return + } + s, err := i.module.Get(ctx, id) + if err != nil { + ctx.JSON(200, &pm3.Response{ + Code: -1, + Success: "fail", + Message: err.Error(), + }) + return + } + tmp, err := i.swagger(ctx, id) + if err != nil { + ctx.JSON(200, &pm3.Response{ + Code: -1, + Success: "fail", + Message: err.Error(), + }) + return + } + + data, _ := tmp.MarshalJSON() + ctx.Status(200) + // 设置响应头 + ctx.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s.json", strings.Replace(s.Name, " ", "_", -1))) + ctx.Header("Content-Type", "application/octet-stream") + ctx.Header("Content-Transfer-Encoding", "binary") + ctx.Writer.Write(data) + return +} + func (i *imlServiceController) Swagger(ctx *gin.Context) { id, has := ctx.Params.Get("id") if !has { @@ -71,9 +127,8 @@ func (i *imlServiceController) Swagger(ctx *gin.Context) { Message: fmt.Sprintf("id is required"), }) return - } - doc, err := i.apiDocModule.GetDoc(ctx, id) + tmp, err := i.swagger(ctx, id) if err != nil { ctx.JSON(200, &pm3.Response{ Code: -1, @@ -82,30 +137,7 @@ func (i *imlServiceController) Swagger(ctx *gin.Context) { }) return } - tmp, err := loader.LoadFromData([]byte(doc.Content)) - if err != nil { - ctx.JSON(200, &pm3.Response{ - Code: -1, - Success: "fail", - Message: err.Error(), - }) - return - } - cfg := i.settingModule.Get(ctx) - - tmp.AddServer(&openapi3.Server{ - URL: cfg.InvokeAddress, - }) - data, err := tmp.MarshalJSON() - if err != nil { - ctx.JSON(200, &pm3.Response{ - Code: -1, - Success: "fail", - Message: err.Error(), - }) - return - } - ctx.JSON(200, string(data)) + ctx.JSON(200, tmp) return } diff --git a/controller/service/service.go b/controller/service/service.go index ddf657f5..4e2a553e 100644 --- a/controller/service/service.go +++ b/controller/service/service.go @@ -28,6 +28,7 @@ type IServiceController interface { MySimple(ctx *gin.Context) ([]*service_dto.SimpleServiceItem, error) Swagger(ctx *gin.Context) + ExportSwagger(ctx *gin.Context) } type IAppController interface { diff --git a/module/cluster/impl.go b/module/cluster/impl.go index db37f50e..4fc11ee3 100644 --- a/module/cluster/impl.go +++ b/module/cluster/impl.go @@ -145,8 +145,8 @@ func (m *imlClusterModule) ClusterNodes(ctx context.Context, clusterId string) ( // return m.Get(ctx, create.Id) //} // -//func (m *imlClusterModule) Search(ctx context.Context, keyword string) ([]*paritiondto.Item, error) { -// partitions, err := m.partitionService.Search(ctx, keyword, nil) +//func (m *imlClusterModule) SearchByDriver(ctx context.Context, keyword string) ([]*paritiondto.Item, error) { +// partitions, err := m.partitionService.SearchByDriver(ctx, keyword, nil) // if err != nil { // return nil, err // } @@ -185,7 +185,7 @@ func (m *imlClusterModule) ClusterNodes(ctx context.Context, clusterId string) ( // if err != nil { // return nil, err // } -// //oDetails, err := m.organizationService.Search(ctx, "") +// //oDetails, err := m.organizationService.SearchByDriver(ctx, "") // //if err != nil { // // return nil, err // //} @@ -248,7 +248,7 @@ func (m *imlClusterModule) ClusterNodes(ctx context.Context, clusterId string) ( //} // //func (m *imlClusterModule) Simple(ctx context.Context) ([]*paritiondto.Simple, error) { -// pm, err := m.partitionService.Search(ctx, "", nil) +// pm, err := m.partitionService.SearchByDriver(ctx, "", nil) // if err != nil { // return nil, err // } @@ -262,7 +262,7 @@ func (m *imlClusterModule) ClusterNodes(ctx context.Context, clusterId string) ( //} // //func (m *imlClusterModule) SimpleByIds(ctx context.Context, ids []string) ([]*paritiondto.Simple, error) { -// pm, err := m.partitionService.Search(ctx, "", map[string]interface{}{ +// pm, err := m.partitionService.SearchByDriver(ctx, "", map[string]interface{}{ // "uuid": ids, // }) // if err != nil { @@ -278,7 +278,7 @@ func (m *imlClusterModule) ClusterNodes(ctx context.Context, clusterId string) ( // //} //func (m *imlClusterModule) SimpleWithCluster(ctx context.Context) ([]*paritiondto.SimpleWithCluster, error) { -// pm, err := m.partitionService.Search(ctx, "", nil) +// pm, err := m.partitionService.SearchByDriver(ctx, "", nil) // if err != nil { // return nil, err // } diff --git a/module/service-diff/iml.go b/module/service-diff/iml.go index b1a91283..8972ed46 100644 --- a/module/service-diff/iml.go +++ b/module/service-diff/iml.go @@ -5,6 +5,8 @@ import ( "errors" "fmt" + "github.com/APIParkLab/APIPark/service/strategy" + "github.com/APIParkLab/APIPark/service/service" "github.com/APIParkLab/APIPark/service/api" @@ -23,6 +25,7 @@ type imlServiceDiff struct { apiDocService api_doc.IAPIDocService `autowired:""` upstreamService upstream.IUpstreamService `autowired:""` releaseService release.IReleaseService `autowired:""` + strategyService strategy.IStrategyService `autowired:""` clusterService cluster.IClusterService `autowired:""` } @@ -79,6 +82,34 @@ func (m *imlServiceDiff) getBaseInfo(ctx context.Context, serviceId, baseRelease return base, nil } + +func (m *imlServiceDiff) latestStrategyCommits(ctx context.Context, serviceId string) ([]*commit.Commit[strategy.StrategyCommit], error) { + list, err := m.strategyService.All(ctx, 2, serviceId) + if err != nil { + return nil, fmt.Errorf("get latest strategy failed:%w", err) + } + + return utils.SliceToSlice(list, func(s *strategy.Strategy) *commit.Commit[strategy.StrategyCommit] { + key := fmt.Sprintf("service-%s", s.Id) + return &commit.Commit[strategy.StrategyCommit]{ + Target: s.Id, + Key: key, + Data: &strategy.StrategyCommit{ + Id: s.Id, + Name: s.Name, + Priority: s.Priority, + Filters: s.Filters, + Config: s.Config, + Driver: s.Driver, + IsStop: s.IsStop, + Version: s.UpdateAt.Format("20060102150405"), + }, + } + }, func(s *strategy.Strategy) bool { + return !s.IsDelete + }), nil +} + func (m *imlServiceDiff) DiffForLatest(ctx context.Context, serviceId string, baseRelease string) (*service_diff.Diff, bool, error) { serviceInfo, err := m.serviceService.Get(ctx, serviceId) if err != nil { @@ -130,6 +161,11 @@ func (m *imlServiceDiff) DiffForLatest(ctx context.Context, serviceId string, ba return nil, false, fmt.Errorf("upstream not found") } + strategyCommits, err := m.latestStrategyCommits(ctx, serviceId) + if err != nil { + return nil, false, err + } + base, err := m.getBaseInfo(ctx, serviceId, baseRelease) if err != nil { return nil, false, err @@ -140,6 +176,7 @@ func (m *imlServiceDiff) DiffForLatest(ctx context.Context, serviceId string, ba apiProxyCommits: proxy, apiDocCommits: apiDocCommits, upstreamCommits: upstreamCommits, + strategyCommits: strategyCommits, } clusters, err := m.clusterService.List(ctx) if err != nil { @@ -211,6 +248,46 @@ func (m *imlServiceDiff) getReleaseInfo(ctx context.Context, releaseId string) ( upstreamCommits: upstreamCommits, }, nil } + +func (m *imlServiceDiff) diffStrategies(base, target []*commit.Commit[strategy.StrategyCommit]) []*service_diff.StrategyDiff { + baseStrategy := utils.SliceToMap(base, func(i *commit.Commit[strategy.StrategyCommit]) string { + return i.Target + }) + targetStrategy := utils.SliceToMap(target, func(i *commit.Commit[strategy.StrategyCommit]) string { + return i.Target + }) + out := make([]*service_diff.StrategyDiff, 0, len(target)) + for _, tc := range targetStrategy { + key := tc.Target + t := tc.Data + o := &service_diff.StrategyDiff{ + Strategy: key, + Name: t.Name, + Priority: t.Priority, + Change: service_diff.ChangeTypeNone, + Status: 0, + } + b, hasB := baseStrategy[key] + if !hasB { + o.Change = service_diff.ChangeTypeNew + } else if b.UUID != tc.UUID { + o.Change = service_diff.ChangeTypeUpdate + } + delete(baseStrategy, key) + out = append(out, o) + } + for _, b := range baseStrategy { + o := &service_diff.StrategyDiff{ + Strategy: b.Target, + Name: b.Data.Name, + Priority: b.Data.Priority, + Change: service_diff.ChangeTypeDelete, + Status: 0, + } + out = append(out, o) + } + return out +} func (m *imlServiceDiff) diff(partitions []string, base, target *projectInfo) *service_diff.Diff { out := &service_diff.Diff{ Apis: nil, @@ -328,6 +405,7 @@ func (m *imlServiceDiff) diff(partitions []string, base, target *projectInfo) *s } } } + out.Strategies = m.diffStrategies(base.strategyCommits, target.strategyCommits) return out } @@ -373,5 +451,13 @@ func (m *imlServiceDiff) Out(ctx context.Context, diff *service_diff.Diff) (*Dif }), }) } + out.Strategies = utils.SliceToSlice(diff.Strategies, func(i *service_diff.StrategyDiff) *StrategyDiffOut { + return &StrategyDiffOut{ + Name: i.Name, + Priority: i.Priority, + Change: i.Change, + Status: i.Status, + } + }) return out, nil } diff --git a/module/service-diff/out.go b/module/service-diff/out.go index a6b98cf3..b4b3dbfb 100644 --- a/module/service-diff/out.go +++ b/module/service-diff/out.go @@ -4,13 +4,15 @@ 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/strategy" "github.com/APIParkLab/APIPark/service/universally/commit" "github.com/APIParkLab/APIPark/service/upstream" ) type DiffOut struct { - Routers []*RouterDiffOut `json:"routers"` - Upstreams []*UpstreamDiffOut `json:"upstreams"` + Routers []*RouterDiffOut `json:"routers"` + Upstreams []*UpstreamDiffOut `json:"upstreams"` + Strategies []*StrategyDiffOut `json:"strategies"` } type RouterDiffOut struct { @@ -30,6 +32,13 @@ type UpstreamDiffOut struct { Addr []string `json:"addr,omitempty"` } +type StrategyDiffOut struct { + Name string `json:"name"` + Priority int `json:"priority"` + Change service_diff.ChangeType `json:"change,omitempty"` + Status service_diff.StatusType `json:"status,omitempty"` +} + type projectInfo struct { id string //apis []*api.Info @@ -37,4 +46,5 @@ type projectInfo struct { apiProxyCommits []*commit.Commit[api.Proxy] apiDocCommits []*commit.Commit[api_doc.DocCommit] upstreamCommits []*commit.Commit[upstream.Config] + strategyCommits []*commit.Commit[strategy.StrategyCommit] } diff --git a/module/service/iml.go b/module/service/iml.go index 652c1a97..377990bd 100644 --- a/module/service/iml.go +++ b/module/service/iml.go @@ -174,7 +174,7 @@ func (i *imlServiceModule) SearchMyServices(ctx context.Context, teamId string, //func (i *imlServiceModule) SimpleAPPS(ctx context.Context, keyword string) ([]*service_dto.SimpleServiceItem, error) { // w := make(map[string]interface{}) // w["as_app"] = true -// services, err := i.serviceService.Search(ctx, keyword, w) +// services, err := i.serviceService.SearchByDriver(ctx, keyword, w) // if err != nil { // return nil, err // } diff --git a/module/strategy/iml.go b/module/strategy/iml.go index 1c3032b4..a8bcbad2 100644 --- a/module/strategy/iml.go +++ b/module/strategy/iml.go @@ -53,7 +53,7 @@ func (i *imlStrategyModule) DeleteServiceStrategy(ctx context.Context, serviceId func (i *imlStrategyModule) ToPublish(ctx context.Context, driver string) ([]*strategy_dto.ToPublishItem, error) { scope := strategy_dto.ToScope(strategy_dto.ScopeGlobal) - list, err := i.strategyService.SearchAll(ctx, "", driver, scope.Int(), "") + list, err := i.strategyService.SearchAllByDriver(ctx, "", driver, scope.Int(), "") if err != nil { return nil, err } @@ -80,7 +80,7 @@ func (i *imlStrategyModule) ToPublish(ctx context.Context, driver string) ([]*st } func (i *imlStrategyModule) Search(ctx context.Context, keyword string, driver string, scope strategy_dto.Scope, target string, page int, pageSize int, filters []string, order ...string) ([]*strategy_dto.StrategyItem, int64, error) { - list, total, err := i.strategyService.Search(ctx, keyword, driver, scope.Int(), target, page, pageSize, filters, order...) + list, total, err := i.strategyService.SearchByDriver(ctx, keyword, driver, scope.Int(), target, page, pageSize, filters, order...) if err != nil { return nil, 0, err } @@ -213,7 +213,7 @@ func (i *imlStrategyModule) Disable(ctx context.Context, id string) error { } func (i *imlStrategyModule) Publish(ctx context.Context, driver string, scope string, target string) error { - list, err := i.strategyService.AllByScope(ctx, driver, strategy_dto.ToScope(scope).Int(), target) + list, err := i.strategyService.AllByDriver(ctx, driver, strategy_dto.ToScope(scope).Int(), target) if err != nil { return err } diff --git a/plugins/core/service.go b/plugins/core/service.go index 11a8ee5d..8fd6dc63 100644 --- a/plugins/core/service.go +++ b/plugins/core/service.go @@ -29,6 +29,7 @@ func (p *plugin) ServiceApis() []pm3.Api { pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/service/doc", []string{"context", "query:service"}, []string{"doc"}, p.serviceController.ServiceDoc, access.SystemWorkspaceServiceViewAll, access.TeamServiceServiceIntroView), pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/service/doc", []string{"context", "query:service", "body"}, nil, p.serviceController.SaveServiceDoc, access.SystemWorkspaceServiceManagerAll, access.TeamServiceServiceIntroManager), - pm3.CreateApiSimple(http.MethodGet, "/swagger/:id", p.serviceController.Swagger), + pm3.CreateApiSimple(http.MethodGet, "/api/v1/service/swagger/:id", p.serviceController.Swagger), + pm3.CreateApiSimple(http.MethodGet, "/api/v1/export/openapi/:id", p.serviceController.ExportSwagger), } } diff --git a/service/service_diff/diff.go b/service/service_diff/diff.go index 2c27ead5..3b6feb03 100644 --- a/service/service_diff/diff.go +++ b/service/service_diff/diff.go @@ -39,8 +39,17 @@ type UpstreamDiff struct { Status StatusType `json:"status,omitempty"` } -type Diff struct { - Clusters []string `json:"clusters,omitempty"` - Apis []*ApiDiff `json:"apis"` - Upstreams []*UpstreamDiff `json:"upstreams"` +type StrategyDiff struct { + Strategy string `json:"strategy,omitempty"` + Name string `json:"name"` + Priority int `json:"priority"` + Change ChangeType `json:"change,omitempty"` + Status StatusType `json:"status,omitempty"` +} + +type Diff struct { + Clusters []string `json:"clusters,omitempty"` + Apis []*ApiDiff `json:"apis"` + Upstreams []*UpstreamDiff `json:"upstreams"` + Strategies []*StrategyDiff `json:"strategies"` } diff --git a/service/strategy/iml.go b/service/strategy/iml.go index ca72ac61..5d435157 100644 --- a/service/strategy/iml.go +++ b/service/strategy/iml.go @@ -22,12 +22,25 @@ type imlStrategyService struct { universally.IServiceEdit[Edit] } +func (i *imlStrategyService) All(ctx context.Context, scope int, target string) ([]*Strategy, error) { + w := make(map[string]interface{}) + w["scope"] = scope + if target != "" { + w["target"] = target + } + list, err := i.store.List(ctx, w) + if err != nil { + return nil, err + } + return utils.SliceToSlice(list, FromEntity), nil +} + func (i *imlStrategyService) Restore(ctx context.Context, id string) error { _, err := i.store.UpdateWhere(ctx, map[string]interface{}{"uuid": id}, map[string]interface{}{"is_delete": false}) return err } -func (i *imlStrategyService) SearchAll(ctx context.Context, keyword string, driver string, scope int, target string) ([]*Strategy, error) { +func (i *imlStrategyService) SearchAllByDriver(ctx context.Context, keyword string, driver string, scope int, target string) ([]*Strategy, error) { w := make(map[string]interface{}) w["scope"] = scope if target != "" { @@ -40,7 +53,7 @@ func (i *imlStrategyService) SearchAll(ctx context.Context, keyword string, driv return utils.SliceToSlice(list, FromEntity), nil } -func (i *imlStrategyService) AllByScope(ctx context.Context, driver string, scope int, target string) ([]*Strategy, error) { +func (i *imlStrategyService) AllByDriver(ctx context.Context, driver string, scope int, target string) ([]*Strategy, error) { w := make(map[string]interface{}) w["scope"] = scope if target != "" { @@ -100,7 +113,7 @@ func (i *imlStrategyService) ListStrategyCommit(ctx context.Context, commitIds . return i.commitService.List(ctx, commitIds...) } -func (i *imlStrategyService) Search(ctx context.Context, keyword string, driver string, scope int, target string, page int, pageSize int, filters []string, order ...string) ([]*Strategy, int64, error) { +func (i *imlStrategyService) SearchByDriver(ctx context.Context, keyword string, driver string, scope int, target string, page int, pageSize int, filters []string, order ...string) ([]*Strategy, int64, error) { w := map[string]interface{}{ "scope": scope, "driver": driver, diff --git a/service/strategy/service.go b/service/strategy/service.go index 817b2732..24501de9 100644 --- a/service/strategy/service.go +++ b/service/strategy/service.go @@ -14,9 +14,10 @@ import ( type IStrategyService interface { universally.IServiceCreate[Create] universally.IServiceEdit[Edit] - AllByScope(ctx context.Context, driver string, scope int, target string) ([]*Strategy, error) - Search(ctx context.Context, keyword string, driver string, scope int, target string, page int, pageSize int, filters []string, order ...string) ([]*Strategy, int64, error) - SearchAll(ctx context.Context, keyword string, driver string, scope int, target string) ([]*Strategy, error) + All(ctx context.Context, scope int, target string) ([]*Strategy, error) + AllByDriver(ctx context.Context, driver string, scope int, target string) ([]*Strategy, error) + SearchByDriver(ctx context.Context, keyword string, driver string, scope int, target string, page int, pageSize int, filters []string, order ...string) ([]*Strategy, int64, error) + SearchAllByDriver(ctx context.Context, keyword string, driver string, scope int, target string) ([]*Strategy, error) Get(ctx context.Context, id string) (*Strategy, error) SortDelete(ctx context.Context, id string) error Delete(ctx context.Context, id ...string) error From 58f192b6e96b73d2088f762dcd8f26b656b5cb4f Mon Sep 17 00:00:00 2001 From: Liujian <824010343@qq.com> Date: Wed, 27 Nov 2024 19:46:52 +0800 Subject: [PATCH 09/14] finish service publish --- module/release/iml.go | 39 +++++++++++++++++++++++++++++++++++++- service/release/commit.go | 1 + service/release/iml.go | 19 ++++++++++++++----- service/release/service.go | 2 +- 4 files changed, 54 insertions(+), 7 deletions(-) diff --git a/module/release/iml.go b/module/release/iml.go index d46f1094..f8a495ea 100644 --- a/module/release/iml.go +++ b/module/release/iml.go @@ -5,6 +5,8 @@ import ( "errors" "fmt" + "github.com/APIParkLab/APIPark/service/strategy" + api_doc "github.com/APIParkLab/APIPark/service/api-doc" service_doc "github.com/APIParkLab/APIPark/service/service-doc" @@ -40,11 +42,39 @@ type imlReleaseModule struct { serviceDocService service_doc.IDocService `autowired:""` upstreamService upstream.IUpstreamService `autowired:""` publishService publish.IPublishService `autowired:""` + strategyService strategy.IStrategyService `autowired:""` transaction store.ITransaction `autowired:""` projectService service.IServiceService `autowired:""` clusterService cluster.IClusterService `autowired:""` } +func (m *imlReleaseModule) latestStrategyCommits(ctx context.Context, serviceId string) ([]*commit.Commit[strategy.StrategyCommit], error) { + list, err := m.strategyService.All(ctx, 2, serviceId) + if err != nil { + return nil, fmt.Errorf("get latest strategy failed:%w", err) + } + + return utils.SliceToSlice(list, func(s *strategy.Strategy) *commit.Commit[strategy.StrategyCommit] { + key := fmt.Sprintf("service-%s", s.Id) + return &commit.Commit[strategy.StrategyCommit]{ + Target: s.Id, + Key: key, + Data: &strategy.StrategyCommit{ + Id: s.Id, + Name: s.Name, + Priority: s.Priority, + Filters: s.Filters, + Config: s.Config, + Driver: s.Driver, + IsStop: s.IsStop, + Version: s.UpdateAt.Format("20060102150405"), + }, + } + }, func(s *strategy.Strategy) bool { + return !s.IsDelete + }), nil +} + func (m *imlReleaseModule) Create(ctx context.Context, serviceId string, input *dto.CreateInput) (string, error) { proInfo, err := m.projectService.Check(ctx, serviceId, projectRuleMustServer) @@ -103,6 +133,13 @@ func (m *imlReleaseModule) Create(ctx context.Context, serviceId string, input * return c.Key, c.UUID }) }) + strategies, err := m.latestStrategyCommits(ctx, serviceId) + if err != nil { + return "", err + } + strategyCommits := utils.SliceToMapO(strategies, func(c *commit.Commit[strategy.StrategyCommit]) (string, string) { + return c.Target, c.UUID + }) var newRelease *release.Release err = m.transaction.Transaction(ctx, func(ctx context.Context) error { @@ -161,7 +198,7 @@ func (m *imlReleaseModule) Create(ctx context.Context, serviceId string, input * 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) + newRelease, err = m.releaseService.CreateRelease(ctx, serviceId, input.Version, input.Remark, requestCommitMap, apiProxyCommits, docCommit.UUID, serviceDocCommit.UUID, upstreamCommitsForUKC, strategyCommits) return err }) if err != nil { diff --git a/service/release/commit.go b/service/release/commit.go index fb8ca5c8..e1feabee 100644 --- a/service/release/commit.go +++ b/service/release/commit.go @@ -8,4 +8,5 @@ const ( CommitUpstream CommitType = "upstream" CommitApiProxy CommitType = "api_proxy" CommitServiceDoc CommitType = "service_doc" + CommitStrategy CommitType = "strategy" ) diff --git a/service/release/iml.go b/service/release/iml.go index 2b8705ba..da0a32ee 100644 --- a/service/release/iml.go +++ b/service/release/iml.go @@ -281,7 +281,7 @@ func (s *imlReleaseService) SetRunning(ctx context.Context, service string, id s } -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) { +func (s *imlReleaseService) CreateRelease(ctx context.Context, service, version, remark string, apiRequestCommit, apisProxyCommits map[string]string, apiDocCommits, serviceDocCommits string, upstreams map[string]map[string]string, strategies map[string]string) (*Release, error) { operator := utils.UserId(ctx) releaseId := uuid.NewString() commits := make([]*release.Commit, 0, len(apisProxyCommits)+len(upstreams)+2) @@ -315,23 +315,32 @@ func (s *imlReleaseService) CreateRelease(ctx context.Context, service string, v }) } } - if apiDocCommit != "" { + if apiDocCommits != "" { commits = append(commits, &release.Commit{ Type: CommitApiDocument, Target: service, Release: releaseId, Key: CommitApiDocument, - Commit: apiDocCommit, + Commit: apiDocCommits, }) } - if serviceDocCommit != "" { + if serviceDocCommits != "" { commits = append(commits, &release.Commit{ Type: CommitServiceDoc, Target: service, Release: releaseId, Key: CommitServiceDoc, - Commit: serviceDocCommit, + Commit: serviceDocCommits, + }) + } + for key, value := range strategies { + commits = append(commits, &release.Commit{ + Type: CommitStrategy, + Target: key, + Release: releaseId, + Key: CommitStrategy, + Commit: value, }) } diff --git a/service/release/service.go b/service/release/service.go index b248d275..79aea897 100644 --- a/service/release/service.go +++ b/service/release/service.go @@ -14,7 +14,7 @@ type IReleaseService interface { // GetRelease 获取发布信息 GetRelease(ctx context.Context, id string) (*Release, error) // CreateRelease 创建发布 - 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) + CreateRelease(ctx context.Context, service, version, remark string, apiRequestCommit, apisProxyCommits map[string]string, apiDocCommits, serviceDocCommits string, upstreams map[string]map[string]string, strategies map[string]string) (*Release, error) // DeleteRelease 删除发布 DeleteRelease(ctx context.Context, id string) error List(ctx context.Context, service string) ([]*Release, error) From 53d895f0462fe17a9ed559ec1c35dc06fa2d9e90 Mon Sep 17 00:00:00 2001 From: Liujian <824010343@qq.com> Date: Wed, 27 Nov 2024 20:18:23 +0800 Subject: [PATCH 10/14] fix rest service publish --- module/release/iml.go | 2 +- module/service-diff/iml.go | 2 +- module/service/filter.go | 23 ++++++++++++++++++++++- service/strategy/iml.go | 2 +- 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/module/release/iml.go b/module/release/iml.go index f8a495ea..374427fc 100644 --- a/module/release/iml.go +++ b/module/release/iml.go @@ -113,7 +113,7 @@ func (m *imlReleaseModule) Create(ctx context.Context, serviceId string, input * return "", errors.New("api or document not found") } - upstreams, err := m.upstreamService.ListLatestCommit(ctx, serviceId) + upstreams, err := m.upstreamService.ListLatestCommit(ctx, cluster.DefaultClusterID, serviceId) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return "", errors.New("api config or document not found") diff --git a/module/service-diff/iml.go b/module/service-diff/iml.go index 8972ed46..3296e2c0 100644 --- a/module/service-diff/iml.go +++ b/module/service-diff/iml.go @@ -153,7 +153,7 @@ func (m *imlServiceDiff) DiffForLatest(ctx context.Context, serviceId string, ba return nil, false, err } - upstreamCommits, err := m.upstreamService.ListLatestCommit(ctx, serviceId) + upstreamCommits, err := m.upstreamService.ListLatestCommit(ctx, cluster.DefaultClusterID, serviceId) if err != nil { return nil, false, err } diff --git a/module/service/filter.go b/module/service/filter.go index 4304a266..e1baf072 100644 --- a/module/service/filter.go +++ b/module/service/filter.go @@ -3,6 +3,8 @@ package service import ( "context" + "github.com/APIParkLab/APIPark/service/subscribe" + service_dto "github.com/APIParkLab/APIPark/module/service/dto" "github.com/eolinker/go-common/auto" @@ -16,7 +18,8 @@ import ( var _ strategy_filter.IRemoteFilter = (*imlAppFilter)(nil) type imlAppFilter struct { - service service.IServiceService `autowired:""` + service service.IServiceService `autowired:""` + subscriberService subscribe.ISubscribeService `autowired:""` } func (i *imlAppFilter) Name() string { @@ -94,6 +97,24 @@ func (i *imlAppFilter) RemoteList(ctx context.Context, keyword string, condition if condition == nil { condition = make(map[string]interface{}) } + if serviceId, ok := condition["service"]; ok { + // 查询订阅了该服务的消费者 + v, ok := serviceId.(string) + if ok { + subscribers, err := i.subscriberService.Subscribers(ctx, v, subscribe.ApplyStatusSubscribe) + if err != nil { + return nil, 0, err + } + if len(subscribers) > 0 { + appIds := utils.SliceToSlice(subscribers, func(s *subscribe.Subscribe) string { + return s.Application + }) + condition["uuid"] = appIds + } + + } + delete(condition, "service") + } condition["as_app"] = true if pageSize == -1 { // 获取全部 diff --git a/service/strategy/iml.go b/service/strategy/iml.go index 5d435157..3398c0a5 100644 --- a/service/strategy/iml.go +++ b/service/strategy/iml.go @@ -212,5 +212,5 @@ func updateHandler(e *strategy.Strategy, i *Edit) { if i.IsStop != nil { e.IsStop = *i.IsStop } - + e.UpdateAt = time.Now() } From 4db7154130c49e641368e11eb59531f7085dde36 Mon Sep 17 00:00:00 2001 From: Liujian <824010343@qq.com> Date: Thu, 28 Nov 2024 11:17:02 +0800 Subject: [PATCH 11/14] add publish tip --- module/release/iml.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/module/release/iml.go b/module/release/iml.go index 374427fc..24fc4a20 100644 --- a/module/release/iml.go +++ b/module/release/iml.go @@ -163,6 +163,9 @@ func (m *imlReleaseModule) Create(ctx context.Context, serviceId string, input * doc, err := m.apiDocService.GetDoc(ctx, serviceId) if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return fmt.Errorf("api doc not found") + } return err } err = m.apiDocService.CommitDoc(ctx, serviceId, doc) @@ -188,7 +191,7 @@ func (m *imlReleaseModule) Create(ctx context.Context, serviceId string, input * } serviceDocCommit, err := m.serviceDocService.LatestDocCommit(ctx, serviceId) if err != nil { - + return err } if !m.releaseService.Completeness(utils.SliceToSlice(clusters, func(s *cluster.Cluster) string { return s.Uuid From 50567665abaff1605c0db623fd8e2d2565839dd4 Mon Sep 17 00:00:00 2001 From: Liujian <824010343@qq.com> Date: Thu, 28 Nov 2024 18:19:08 +0800 Subject: [PATCH 12/14] =?UTF-8?q?openapi=E5=8F=96=E6=B6=88=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/core/service.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/core/service.go b/plugins/core/service.go index 8fd6dc63..e929e6ef 100644 --- a/plugins/core/service.go +++ b/plugins/core/service.go @@ -3,11 +3,14 @@ package core import ( "net/http" + "github.com/eolinker/go-common/ignore" + "github.com/APIParkLab/APIPark/resources/access" "github.com/eolinker/go-common/pm3" ) func (p *plugin) ServiceApis() []pm3.Api { + ignore.IgnorePath("login", http.MethodGet, "/api/v1/service/swagger/:id") return []pm3.Api{ // 项目 pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/service/info", []string{"context", "query:service"}, []string{"service"}, p.serviceController.Get, access.SystemWorkspaceServiceViewAll, access.TeamTeamServiceView), From f46fe0ec4cb25f4bcc2e0a239e1c96031cf50c6f Mon Sep 17 00:00:00 2001 From: Liujian <824010343@qq.com> Date: Thu, 28 Nov 2024 18:34:15 +0800 Subject: [PATCH 13/14] fix strategy bug --- module/service-diff/iml.go | 3 +++ module/service/filter.go | 4 ++-- module/strategy/iml.go | 3 --- plugins/core/strategy.go | 1 + 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/module/service-diff/iml.go b/module/service-diff/iml.go index 3296e2c0..168bbadd 100644 --- a/module/service-diff/iml.go +++ b/module/service-diff/iml.go @@ -119,6 +119,9 @@ func (m *imlServiceDiff) DiffForLatest(ctx context.Context, serviceId string, ba if err != nil { return nil, false, err } + if len(apis) < 1 { + return nil, false, fmt.Errorf("api not found") + } apiIds := utils.SliceToSlice(apis, func(i *api.API) string { return i.UUID diff --git a/module/service/filter.go b/module/service/filter.go index e1baf072..667fcb63 100644 --- a/module/service/filter.go +++ b/module/service/filter.go @@ -27,7 +27,7 @@ func (i *imlAppFilter) Name() string { } func (i *imlAppFilter) Title() string { - return "消费者" + return "consumer" } func (i *imlAppFilter) Labels(values ...string) []string { @@ -36,7 +36,7 @@ func (i *imlAppFilter) Labels(values ...string) []string { } if values[0] == strategy_filter.ValuesALL { return []string{ - "全部消费者", + "all consumer", } } apps, err := i.service.AppList(context.Background(), values...) diff --git a/module/strategy/iml.go b/module/strategy/iml.go index a8bcbad2..41b99d4e 100644 --- a/module/strategy/iml.go +++ b/module/strategy/iml.go @@ -226,9 +226,6 @@ func (i *imlStrategyModule) Publish(ctx context.Context, driver string, scope st } } - if l.IsStop { - continue - } // TODO:同步到网关 err = i.strategyService.CommitStrategy(txCtx, scope, target, l.Id, l) if err != nil { diff --git a/plugins/core/strategy.go b/plugins/core/strategy.go index 2ed88374..d9e9a074 100644 --- a/plugins/core/strategy.go +++ b/plugins/core/strategy.go @@ -21,6 +21,7 @@ func (p *plugin) strategyApis() []pm3.Api { pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/strategy/service/:driver/list", []string{"context", "query:keyword", "query:service", "rest:driver", "query:page", "query:page_size", "query:order", "query:sort", "query:filters"}, []string{"list", "total"}, p.strategyController.ServiceStrategyList), pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/strategy/service/:driver", []string{"context", "query:strategy"}, []string{"strategy"}, p.strategyController.GetStrategy), pm3.CreateApiWidthDoc(http.MethodPost, "/api/v1/strategy/service/:driver", []string{"context", "query:service", "rest:driver", "body"}, nil, p.strategyController.CreateServiceStrategy), + pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/strategy/service/:driver", []string{"context", "query:strategy", "body"}, nil, p.strategyController.EditStrategy), pm3.CreateApiWidthDoc(http.MethodPatch, "/api/v1/strategy/service/:driver/enable", []string{"context", "query:strategy"}, nil, p.strategyController.EnableStrategy), pm3.CreateApiWidthDoc(http.MethodPatch, "/api/v1/strategy/service/:driver/disable", []string{"context", "query:strategy"}, nil, p.strategyController.DisableStrategy), pm3.CreateApiWidthDoc(http.MethodDelete, "/api/v1/strategy/service/:driver", []string{"context", "query:service", "query:strategy"}, nil, p.strategyController.DeleteServiceStrategy), From 777256bf309304008e4db2907e437cbd0292487b Mon Sep 17 00:00:00 2001 From: Liujian <824010343@qq.com> Date: Thu, 28 Nov 2024 18:40:00 +0800 Subject: [PATCH 14/14] update transaction --- resources/locale/i18n/en-US.json | 15 ++++++-- resources/locale/i18n/ja-JP.json | 35 +++++++++++------- resources/locale/i18n/zh-CN.json | 1 + resources/locale/i18n/zh-TW.json | 61 ++++++++++++++++++-------------- 4 files changed, 70 insertions(+), 42 deletions(-) diff --git a/resources/locale/i18n/en-US.json b/resources/locale/i18n/en-US.json index c9f6adff..81cf2b43 100644 --- a/resources/locale/i18n/en-US.json +++ b/resources/locale/i18n/en-US.json @@ -5,12 +5,19 @@ "api": "API", "api doc": "API Documentation", "api gateway": "API Gateway", + "api method": "API Request Method", + "api name": "API Name", + "api path": "API Path", + "all consumer": "All Consumers", + "all method": "All Methods", "api portal": "API Portal", - "authorization": "Access Authorization", + "authorization": "Authorization", "consumer": "Consumer", + "consumer id": "Consumer ID", "consumer admin": "Consumer Admin", "consumer developer": "Consumer Developer", "create": "Create", + "description": "Description", "data source": "Data Source", "devops admin": "DevOps Admin", "general": "General Settings", @@ -22,7 +29,7 @@ "member": "Member", "release": "Release", "role": "Role", - "run view": "Run View", + "run view": "Runtime View", "service": "Service", "service admin": "Service Admin", "service developer": "Service Developer", @@ -42,5 +49,7 @@ "view all service": "View All Services", "view all team": "View All Teams", "view subscribed services": "View Subscribed Services", - "workspace": "Workspace" + "workspace": "Workspace", + "request path": "Request Path", + "methods": "Request Methods" } diff --git a/resources/locale/i18n/ja-JP.json b/resources/locale/i18n/ja-JP.json index a7f00ec6..7d9b18f3 100644 --- a/resources/locale/i18n/ja-JP.json +++ b/resources/locale/i18n/ja-JP.json @@ -5,31 +5,38 @@ "api": "API", "api doc": "APIドキュメント", "api gateway": "APIゲートウェイ", + "api method": "APIリクエストメソッド", + "api name": "API名", + "api path": "APIパス", + "all consumer": "全てのコンシューマー", + "all method": "全てのリクエスト方式", "api portal": "APIポータル", - "authorization": "アクセス許可", - "consumer": "消費者", - "consumer admin": "消費者管理者", - "consumer developer": "消費者開発者", + "authorization": "認証", + "consumer": "コンシューマー", + "consumer id": "コンシューマーID", + "consumer admin": "コンシューマー管理者", + "consumer developer": "コンシューマーデベロッパー", "create": "作成", + "description": "説明", "data source": "データソース", "devops admin": "DevOps管理者", "general": "一般設定", "general member": "一般メンバー", "log configuration": "ログ設定", "manager": "管理者", - "manager all consumer": "すべての消費者を管理", - "manager subscribed services": "購読したサービスを管理", + "manager all consumer": "全てのコンシューマーを管理", + "manager subscribed services": "購読済みサービスを管理", "member": "メンバー", "release": "リリース", "role": "役割", "run view": "実行ビュー", "service": "サービス", "service admin": "サービス管理者", - "service developer": "サービス開発者", + "service developer": "サービスデベロッパー", "service intro": "サービスドキュメント", "ssl certificate": "SSL証明書", "allow subscribe service": "サービスの購読を許可", - "subscription review": "購読レビュー", + "subscription review": "購読審査", "subscription service": "購読サービス", "super admin": "スーパ管理者", "system settings": "システム設定", @@ -38,9 +45,11 @@ "team admin": "チーム管理者", "upstream": "アップストリーム", "view": "表示", - "view all consumer": "すべての消費者を表示", - "view all service": "すべてのサービスを表示", - "view all team": "すべてのチームを表示", - "view subscribed services": "購読したサービスを表示", - "workspace": "ワークスペース" + "view all consumer": "全てのコンシューマーを表示", + "view all service": "全てのサービスを表示", + "view all team": "全てのチームを表示", + "view subscribed services": "購読済みサービスを表示", + "workspace": "ワークスペース", + "request path": "リクエストパス", + "methods": "リクエスト方式" } diff --git a/resources/locale/i18n/zh-CN.json b/resources/locale/i18n/zh-CN.json index 52bf043b..516ce71a 100644 --- a/resources/locale/i18n/zh-CN.json +++ b/resources/locale/i18n/zh-CN.json @@ -8,6 +8,7 @@ "api method": "API请求方法", "api name": "API名称", "api path": "API路径", + "all consumer": "所有消费者", "all method": "全部请求方式", "api portal": "API门户", "authorization": "访问授权", diff --git a/resources/locale/i18n/zh-TW.json b/resources/locale/i18n/zh-TW.json index 36a21ad4..aee3c20a 100644 --- a/resources/locale/i18n/zh-TW.json +++ b/resources/locale/i18n/zh-TW.json @@ -1,46 +1,55 @@ { "account": "帳號", - "ai provider": "AI 供應商", + "ai provider": "AI 模型供應商", "analysis": "分析報告", "api": "API", "api doc": "API 文件", "api gateway": "API 閘道", - "api portal": "API 入口網站", - "authorization": "存取授權", - "consumer": "使用者", - "consumer admin": "使用者管理員", - "consumer developer": "使用者開發人員", - "create": "建立", - "data source": "資料來源", - "devops admin": "DevOps 管理員", - "general": "一般設定", - "general member": "一般成員", - "log configuration": "日誌設定", + "api method": "API 請求方法", + "api name": "API 名稱", + "api path": "API 路徑", + "all consumer": "所有消費者", + "all method": "全部請求方式", + "api portal": "API 門戶", + "authorization": "訪問授權", + "consumer": "消費者", + "consumer id": "消費者 ID", + "consumer admin": "消費者管理員", + "consumer developer": "消費者開發者", + "create": "創建", + "description": "描述", + "data source": "資料源", + "devops admin": "運維管理員", + "general": "常規設置", + "general member": "普通成員", + "log configuration": "日誌配置", "manager": "管理", - "manager all consumer": "管理所有使用者", + "manager all consumer": "管理所有消費者", "manager subscribed services": "管理已訂閱的服務", "member": "成員", - "release": "發佈", + "release": "發布", "role": "角色", - "run view": "執行檢視", + "run view": "運行視圖", "service": "服務", "service admin": "服務管理員", - "service developer": "服務開發人員", - "service intro": "服務說明文件", - "ssl certificate": "SSL 憑證", + "service developer": "服務開發者", + "service intro": "服務文檔", + "ssl certificate": "SSL 證書", "allow subscribe service": "允許訂閱服務", "subscription review": "訂閱審核", "subscription service": "訂閱服務", "super admin": "超級管理員", - "system settings": "系統設定", + "system settings": "系統設置", "team": "團隊", - "team settings": "團隊設定", + "team settings": "團隊設置", "team admin": "團隊管理員", "upstream": "上游", - "view": "檢視", - "view all consumer": "檢視所有使用者", - "view all service": "檢視所有服務", - "view all team": "檢視所有團隊", - "view subscribed services": "檢視已訂閱的服務", - "workspace": "工作區" + "view": "查看", + "view all consumer": "查看所有消費者", + "view all service": "查看所有服務", + "view all team": "查看所有團隊", + "view subscribed services": "查看已訂閱的服務", + "workspace": "工作空間", + "request path": "請求路徑", + "methods": "請求方法" }