This is an automated email from the ASF dual-hosted git repository.

robocanic pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/dubbo-admin.git


The following commit(s) were added to refs/heads/develop by this push:
     new 4aae2061 feat: Defined a unified error (#1353)
4aae2061 is described below

commit 4aae2061efc9cd7a4ca406c729c481e18c3d6fe0
Author: robb <[email protected]>
AuthorDate: Sun Nov 9 20:08:49 2025 +0800

    feat: Defined a unified error (#1353)
    
    * feat: unified error code; separate application handler and service into 
parts
    
    * fix: license header lack
    
    * fix: copilot review err fix
    
    * fix: simplify the if-else condition
    
    * chore: rename Error() to String()
    
    * chore: add extra String() in Error
    
    * feat: 新增listMeshes接口
    
    * fix: copilot review
---
 app/dubbo-admin/dubbo-admin.yaml                   |  24 +-
 pkg/common/bizerror/common.go                      |  35 +-
 pkg/common/bizerror/{common.go => error.go}        |  55 ++-
 pkg/config/discovery/config.go                     |  23 +-
 pkg/console/component.go                           |   4 +-
 pkg/console/handler/application.go                 | 458 ++++-----------------
 pkg/console/handler/auth.go                        |  38 +-
 .../bizerror/common.go => console/handler/mesh.go} |  52 +--
 pkg/console/model/application.go                   |  12 +
 pkg/console/model/common.go                        |  28 +-
 pkg/console/model/condition_rule.go                |  13 +-
 pkg/console/model/configurator_rule.go             |   9 +-
 .../bizerror/common.go => console/model/mesh.go}   |  40 +-
 pkg/console/model/tag_rule.go                      |   8 +-
 pkg/console/router/router.go                       |   5 +-
 pkg/console/service/application.go                 | 408 ++++++++++++++++++
 .../bizerror/common.go => console/util/error.go}   |  44 +-
 17 files changed, 650 insertions(+), 606 deletions(-)

diff --git a/app/dubbo-admin/dubbo-admin.yaml b/app/dubbo-admin/dubbo-admin.yaml
index 932304ea..17895772 100644
--- a/app/dubbo-admin/dubbo-admin.yaml
+++ b/app/dubbo-admin/dubbo-admin.yaml
@@ -39,19 +39,23 @@ console:
 store:
   type: memory
 discovery:
-  - type: nacos
-    id: nacos-44.33
-    address:
-      registry: nacos://47.76.94.134:8848?username=nacos&password=nacos
-      configCenter: nacos://47.76.94.134:8848?username=nacos&password=nacos
-      metadataReport: nacos://47.76.94.134:8848?username=nacos&password=nacos
-
-  - type: etcd
-    id: etcd-44.33
-    address: http://127.0.0.1:2379
+#  - type: nacos
+#    name: nacos-44.33
+#    address:
+#      registry: nacos://47.76.94.134:8848?username=nacos&password=nacos
+#      configCenter: nacos://47.76.94.134:8848?username=nacos&password=nacos
+#      metadataReport: nacos://47.76.94.134:8848?username=nacos&password=nacos
+#
+#  - type: etcd
+#    name: etcd-44.33
+#    address:
+#      registry: http://127.0.0.1:2379
+#      configCenter: http://127.0.0.1:2379
+#      metadataReport: http://127.0.0.1:2379
 
   # mock discovery is only for development
   - type: mock
+    name: mockRegistry
 engine:
   name: k8s1.28.6
   type: kubernetes
diff --git a/pkg/common/bizerror/common.go b/pkg/common/bizerror/common.go
index 1f993c4b..b8201e95 100644
--- a/pkg/common/bizerror/common.go
+++ b/pkg/common/bizerror/common.go
@@ -18,38 +18,15 @@
 package bizerror
 
 import (
-       "errors"
        "fmt"
 )
 
-type AssertionError struct {
-       msg string
+func NewAssertionError(expected, actual interface{}) Error {
+       return NewBizError(UnknownError, fmt.Sprintf("type assertion error, 
expected:%v, actual:%v", expected, actual))
 }
-
-func NewAssertionError(expected, actual interface{}) error {
-       return &AssertionError{
-               msg: fmt.Sprintf("type assertion error, expected:%v, 
actual:%v", expected, actual),
-       }
-}
-
-func (e *AssertionError) Error() string {
-       return e.msg
-}
-
-type MeshNotFoundError struct {
-       Mesh string
+func NewUnauthorizedError() Error {
+       return NewBizError(Unauthorized, "no access, please login")
 }
-
-func (m *MeshNotFoundError) Error() string {
-       return fmt.Sprintf("mesh of name %s is not found", m.Mesh)
-}
-
-func MeshNotFound(meshName string) error {
-       return &MeshNotFoundError{meshName}
-}
-
-func IsMeshNotFound(err error) bool {
-       var meshNotFoundError *MeshNotFoundError
-       ok := errors.As(err, &meshNotFoundError)
-       return ok
+func MeshNotFoundError(mesh string) Error {
+       return NewBizError(UnknownError, fmt.Sprintf("mesh of name %s is not 
found", mesh))
 }
diff --git a/pkg/common/bizerror/common.go b/pkg/common/bizerror/error.go
similarity index 50%
copy from pkg/common/bizerror/common.go
copy to pkg/common/bizerror/error.go
index 1f993c4b..b65b023d 100644
--- a/pkg/common/bizerror/common.go
+++ b/pkg/common/bizerror/error.go
@@ -17,39 +17,50 @@
 
 package bizerror
 
-import (
-       "errors"
-       "fmt"
+type Error interface {
+       Code() ErrorCode
+       Message() string
+       Error() string
+       String() string
+}
+
+type ErrorCode string
+
+const (
+       UnknownError    ErrorCode = "UnknownError"
+       InvalidArgument ErrorCode = "InvalidArgument"
+       StoreError      ErrorCode = "StoreError"
+       AppNotFound     ErrorCode = "AppNotFound"
+       Unauthorized    ErrorCode = "Unauthorized"
+       SessionError    ErrorCode = "SessionError"
 )
 
-type AssertionError struct {
-       msg string
+type bizError struct {
+       code    ErrorCode
+       message string
 }
 
-func NewAssertionError(expected, actual interface{}) error {
-       return &AssertionError{
-               msg: fmt.Sprintf("type assertion error, expected:%v, 
actual:%v", expected, actual),
-       }
-}
+var _ Error = &bizError{}
 
-func (e *AssertionError) Error() string {
-       return e.msg
+func NewBizError(code ErrorCode, message string) Error {
+       return &bizError{
+               code:    code,
+               message: message,
+       }
 }
 
-type MeshNotFoundError struct {
-       Mesh string
+func (b *bizError) Code() ErrorCode {
+       return b.code
 }
 
-func (m *MeshNotFoundError) Error() string {
-       return fmt.Sprintf("mesh of name %s is not found", m.Mesh)
+func (b *bizError) Message() string {
+       return b.message
 }
 
-func MeshNotFound(meshName string) error {
-       return &MeshNotFoundError{meshName}
+func (b *bizError) Error() string {
+       return b.String()
 }
 
-func IsMeshNotFound(err error) bool {
-       var meshNotFoundError *MeshNotFoundError
-       ok := errors.As(err, &meshNotFoundError)
-       return ok
+func (b *bizError) String() string {
+       return string(b.code) + ": " + b.message
 }
diff --git a/pkg/config/discovery/config.go b/pkg/config/discovery/config.go
index fe4dfd00..ee63eed4 100644
--- a/pkg/config/discovery/config.go
+++ b/pkg/config/discovery/config.go
@@ -17,7 +17,12 @@
 
 package discovery
 
-import "github.com/apache/dubbo-admin/pkg/config"
+import (
+       "github.com/duke-git/lancet/v2/strutil"
+
+       "github.com/apache/dubbo-admin/pkg/common/bizerror"
+       "github.com/apache/dubbo-admin/pkg/config"
+)
 
 type Type string
 
@@ -31,9 +36,9 @@ const (
 // Config defines Discovery configuration
 type Config struct {
        config.BaseConfig
-       Name    string `json:"name"`
-       Type    Type   `json:"type"`
-       Address AddressConfig
+       Name    string        `json:"name"`
+       Type    Type          `json:"type"`
+       Address AddressConfig `json:"address"`
 }
 
 // AddressConfig defines Discovery Engine address
@@ -54,3 +59,13 @@ func DefaultDiscoveryEnginConfig() *Config {
                },
        }
 }
+
+func (c *Config) Validate() error {
+       if strutil.IsBlank(c.Name) {
+               return bizerror.NewBizError(bizerror.InvalidArgument, 
"discovery name is needed")
+       }
+       if strutil.IsBlank(string(c.Type)) {
+               return bizerror.NewBizError(bizerror.InvalidArgument, 
"discovery type is needed")
+       }
+       return nil
+}
diff --git a/pkg/console/component.go b/pkg/console/component.go
index 1672b95f..985b78f6 100644
--- a/pkg/console/component.go
+++ b/pkg/console/component.go
@@ -30,6 +30,7 @@ import (
        "github.com/gin-gonic/gin"
 
        ui "github.com/apache/dubbo-admin/app/dubbo-ui"
+       "github.com/apache/dubbo-admin/pkg/common/bizerror"
        "github.com/apache/dubbo-admin/pkg/config/console"
        consolectx "github.com/apache/dubbo-admin/pkg/console/context"
        "github.com/apache/dubbo-admin/pkg/console/model"
@@ -130,7 +131,8 @@ func (c *consoleWebServer) authMiddleware() gin.HandlerFunc 
{
                session := sessions.Default(c)
                user := session.Get("user")
                if user == nil {
-                       c.JSON(http.StatusUnauthorized, 
model.NewUnauthorizedResp())
+                       authErr := bizerror.NewBizError(bizerror.Unauthorized, 
"no access, please login")
+                       c.JSON(http.StatusUnauthorized, 
model.NewBizErrorResp(authErr))
                        c.Abort()
                        return
                }
diff --git a/pkg/console/handler/application.go 
b/pkg/console/handler/application.go
index 1eedc906..4ad2985a 100644
--- a/pkg/console/handler/application.go
+++ b/pkg/console/handler/application.go
@@ -18,32 +18,29 @@
 package handler
 
 import (
+       "errors"
        "net/http"
        "strconv"
 
        "github.com/duke-git/lancet/v2/strutil"
        "github.com/gin-gonic/gin"
 
-       meshproto "github.com/apache/dubbo-admin/api/mesh/v1alpha1"
        consolectx "github.com/apache/dubbo-admin/pkg/console/context"
        "github.com/apache/dubbo-admin/pkg/console/model"
        "github.com/apache/dubbo-admin/pkg/console/service"
-       "github.com/apache/dubbo-admin/pkg/core/consts"
-       "github.com/apache/dubbo-admin/pkg/core/logger"
-       meshresource 
"github.com/apache/dubbo-admin/pkg/core/resource/apis/mesh/v1alpha1"
-       corestore "github.com/apache/dubbo-admin/pkg/core/store"
+       "github.com/apache/dubbo-admin/pkg/console/util"
 )
 
 func GetApplicationDetail(ctx consolectx.Context) gin.HandlerFunc {
        return func(c *gin.Context) {
                req := &model.ApplicationDetailReq{}
                if err := c.ShouldBindQuery(req); err != nil {
-                       c.JSON(http.StatusBadRequest, 
model.NewErrorResp(err.Error()))
+                       util.HandleArgumentError(c, err)
                        return
                }
                resp, err := service.GetApplicationDetail(ctx, req)
                if err != nil {
-                       c.JSON(http.StatusInternalServerError, 
model.NewErrorResp(err.Error()))
+                       util.HandleServiceError(c, err)
                        return
                }
                c.JSON(http.StatusOK, model.NewSuccessResp(resp))
@@ -54,13 +51,13 @@ func GetApplicationTabInstanceInfo(ctx consolectx.Context) 
gin.HandlerFunc {
        return func(c *gin.Context) {
                req := model.NewApplicationTabInstanceInfoReq()
                if err := c.ShouldBindQuery(req); err != nil {
-                       c.JSON(http.StatusBadRequest, 
model.NewErrorResp(err.Error()))
+                       util.HandleArgumentError(c, err)
                        return
                }
 
                resp, err := service.GetAppInstanceInfo(ctx, req)
                if err != nil {
-                       c.JSON(http.StatusInternalServerError, 
model.NewErrorResp(err.Error()))
+                       util.HandleServiceError(c, err)
                        return
                }
 
@@ -72,12 +69,12 @@ func GetApplicationServiceForm(ctx consolectx.Context) 
gin.HandlerFunc {
        return func(c *gin.Context) {
                req := model.NewApplicationServiceFormReq()
                if err := c.ShouldBindQuery(req); err != nil {
-                       c.JSON(http.StatusBadRequest, 
model.NewErrorResp(err.Error()))
+                       util.HandleArgumentError(c, err)
                        return
                }
                resp, err := service.GetAppServiceInfo(ctx, req)
                if err != nil {
-                       c.JSON(http.StatusBadRequest, 
model.NewErrorResp(err.Error()))
+                       util.HandleServiceError(c, err)
                        return
                }
                c.JSON(http.StatusOK, model.NewSuccessResp(resp))
@@ -88,468 +85,159 @@ func ApplicationSearch(ctx consolectx.Context) 
gin.HandlerFunc {
        return func(c *gin.Context) {
                req := model.NewApplicationSearchReq()
                if err := c.ShouldBindQuery(req); err != nil {
-                       c.JSON(http.StatusBadRequest, 
model.NewErrorResp(err.Error()))
+                       util.HandleArgumentError(c, err)
                        return
                }
 
                resp, err := service.SearchApplications(ctx, req)
                if err != nil {
-                       c.JSON(http.StatusBadRequest, 
model.NewErrorResp(err.Error()))
+                       util.HandleServiceError(c, err)
                        return
                }
                c.JSON(http.StatusOK, model.NewSuccessResp(resp))
        }
 }
 
-func isAppOperatorLogOpened(conf *meshproto.OverrideConfig, appName string) 
bool {
-       if conf.Side != consts.SideProvider ||
-               conf.Parameters == nil ||
-               conf.Match == nil ||
-               conf.Match.Application == nil ||
-               conf.Match.Application.Oneof == nil ||
-               len(conf.Match.Application.Oneof) != 1 ||
-               conf.Match.Application.Oneof[0].Exact != appName {
-               return false
-       } else if val, ok := conf.Parameters[`accesslog`]; !ok || val != `true` 
{
-               return false
-       }
-       return true
-}
-
-func ApplicationConfigOperatorLogPut(ctx consolectx.Context) gin.HandlerFunc {
+func ApplicationConfigAccessLogPut(ctx consolectx.Context) gin.HandlerFunc {
        return func(c *gin.Context) {
-               var (
-                       appName         string
-                       operatorLogOpen bool
-                       isNotExist      = false
-                       mesh            string
-               )
-               appName = c.Query("appName")
-               mesh = c.Query("mesh")
-               operatorLogOpen, err := 
strconv.ParseBool(c.Query("operatorLog"))
-               if err != nil {
-                       c.JSON(http.StatusBadRequest, 
model.NewErrorResp(err.Error()))
+               appName := c.Query("appName")
+               if strutil.IsBlank(appName) {
+                       util.HandleArgumentError(c, errors.New("appName is 
required"))
                        return
                }
-               appConfiguratorName := appName + consts.ConfiguratorRuleSuffix
-               res, err := service.GetConfigurator(ctx, appConfiguratorName, 
mesh)
-               if err != nil {
-                       if corestore.IsResourceNotFound(err) {
-                               // check app exists
-                               data, err := service.GetApplicationDetail(ctx, 
&model.ApplicationDetailReq{AppName: appName})
-                               if err != nil || data == nil {
-                                       c.JSON(http.StatusNotFound, 
model.NewErrorResp(err.Error()))
-                                       return
-                               }
-                               res = 
meshresource.NewDynamicConfigResourceWithAttributes(appConfiguratorName, mesh)
-                               res.Spec = &meshproto.DynamicConfig{
-                                       Key:           appName,
-                                       Scope:         consts.ScopeApplication,
-                                       ConfigVersion: 
consts.ConfiguratorVersionV3,
-                                       Enabled:       true,
-                                       Configs:       
make([]*meshproto.OverrideConfig, 0),
-                               }
-                               isNotExist = true
-                       } else {
-                               c.JSON(http.StatusInternalServerError, 
model.NewErrorResp(err.Error()))
-                               return
-                       }
+               mesh := c.Query("mesh")
+               if strutil.IsBlank(mesh) {
+                       util.HandleArgumentError(c, errors.New("mesh is 
required"))
+                       return
                }
-               // append or remove
-               if operatorLogOpen {
-                       // check is already exist
-                       alreadyExist := false
-                       res.Spec.RangeConfig(func(conf 
*meshproto.OverrideConfig) (isStop bool) {
-                               alreadyExist = isAppOperatorLogOpened(conf, 
appName)
-                               return alreadyExist
-                       })
-                       if alreadyExist {
-                               c.JSON(http.StatusOK, model.NewSuccessResp(nil))
-                               return
-                       }
-                       if res.Spec.Configs == nil {
-                               res.Spec.Configs = 
make([]*meshproto.OverrideConfig, 0)
-                       }
-                       res.Spec.Configs = append(res.Spec.Configs, 
&meshproto.OverrideConfig{
-                               Side:       consts.SideProvider,
-                               Parameters: map[string]string{`accesslog`: 
`true`},
-                               Enabled:    true,
-                               Match: &meshproto.ConditionMatch{
-                                       Application: &meshproto.ListStringMatch{
-                                               Oneof: []*meshproto.StringMatch{
-                                                       {
-                                                               Exact: appName,
-                                                       },
-                                               }}},
-                               XGenerateByCp: true,
-                       })
-               } else {
-                       res.Spec.RangeConfigsToRemove(func(conf 
*meshproto.OverrideConfig) (IsRemove bool) {
-                               if conf == nil {
-                                       return true
-                               }
-                               return isAppOperatorLogOpened(conf, appName)
-                       })
+               operatorLogOpen, err := 
strconv.ParseBool(c.Query("operatorLog"))
+               if err != nil {
+                       util.HandleArgumentError(c, err)
+                       return
                }
-               // restore
-               if isNotExist {
-                       err = service.CreateConfigurator(ctx, appName, res)
-                       if err != nil {
-                               c.JSON(http.StatusInternalServerError, 
model.NewErrorResp(err.Error()))
-                               return
-                       }
-               } else {
-                       err = service.UpdateConfigurator(ctx, appName, res)
-                       if err != nil {
-                               c.JSON(http.StatusInternalServerError, 
model.NewErrorResp(err.Error()))
-                               return
-                       }
+               if err := service.UpInsertAppAccessLog(ctx, appName, 
operatorLogOpen, mesh); err != nil {
+                       util.HandleServiceError(c, err)
+                       return
                }
-               c.JSON(http.StatusOK, model.NewSuccessResp(nil))
+               c.JSON(http.StatusOK, model.NewSuccessResp(true))
        }
 }
 
-func ApplicationConfigOperatorLogGet(ctx consolectx.Context) gin.HandlerFunc {
+func ApplicationConfigAccessLogGet(ctx consolectx.Context) gin.HandlerFunc {
        return func(c *gin.Context) {
                appName := c.Query("appName")
-               if appName == "" {
-                       c.JSON(http.StatusBadRequest, 
model.NewErrorResp("appName is required"))
+               if strutil.IsBlank(appName) {
+                       util.HandleArgumentError(c, errors.New("appName is 
required"))
                        return
                }
                mesh := c.Query("mesh")
                if strutil.IsBlank(mesh) {
-                       c.JSON(http.StatusBadRequest, model.NewErrorResp("mesh 
is required"))
+                       util.HandleArgumentError(c, errors.New("mesh is 
required"))
                        return
                }
-               appConfiguratorName := appName + consts.ConfiguratorRuleSuffix
-               res, err := service.GetConfigurator(ctx, appConfiguratorName, 
mesh)
+               resp, err := service.GetAppAccessLog(ctx, appName, mesh)
                if err != nil {
-                       if corestore.IsResourceNotFound(err) {
-                               c.JSON(http.StatusOK, 
model.NewSuccessResp(map[string]interface{}{"operatorLog": false}))
-                               return
-                       }
-                       c.JSON(http.StatusNotFound, 
model.NewErrorResp(err.Error()))
+                       util.HandleServiceError(c, err)
                        return
                }
-               isExist := false
-               res.Spec.RangeConfig(func(conf *meshproto.OverrideConfig) 
(isStop bool) {
-                       if isExist = isAppOperatorLogOpened(conf, appName); 
isExist {
-                               return true
-                       }
-                       return false
-               })
-               c.JSON(http.StatusOK, 
model.NewSuccessResp(map[string]interface{}{"operatorLog": isExist}))
+               c.JSON(http.StatusOK, model.NewSuccessResp(resp))
        }
 }
 
 func ApplicationConfigFlowWeightGET(ctx consolectx.Context) gin.HandlerFunc {
        return func(c *gin.Context) {
-               var (
-                       appName string
-                       mesh    string
-                       resp    = struct {
-                               FlowWeightSets []model.FlowWeightSet 
`json:"flowWeightSets"`
-                       }{}
-               )
-               appName = c.Query("appName")
-               mesh = c.Query("mesh")
-               if appName == "" {
-                       c.JSON(http.StatusBadRequest, 
model.NewErrorResp("appName is required"))
+               appName := c.Query("appName")
+               mesh := c.Query("mesh")
+               if strutil.IsBlank(appName) {
+                       util.HandleArgumentError(c, errors.New("appName is 
required"))
                        return
                }
-
-               resp.FlowWeightSets = make([]model.FlowWeightSet, 0)
-               appConfiguratorName := appName + consts.ConfiguratorRuleSuffix
-               res, err := service.GetConfigurator(ctx, appConfiguratorName, 
mesh)
-               if err != nil {
-                       if corestore.IsResourceNotFound(err) {
-                               c.JSON(http.StatusOK, 
model.NewSuccessResp(resp))
-                               return
-                       }
-                       c.JSON(http.StatusNotFound, 
model.NewErrorResp(err.Error()))
+               if strutil.IsBlank(mesh) {
+                       util.HandleArgumentError(c, errors.New("mesh is 
required"))
                        return
                }
-
-               weight := 0
-               res.Spec.RangeConfig(func(conf *meshproto.OverrideConfig) 
(isStop bool) {
-                       if isFlowWeight(conf) {
-                               weight, err = 
strconv.Atoi(conf.Parameters[`weight`])
-                               if err != nil {
-                                       logger.Error("parse weight failed", err)
-                                       return true
-                               }
-                               scope := make([]model.ParamMatch, 0, 
len(conf.Match.Param))
-                               for _, param := range conf.Match.Param {
-                                       scope = append(scope, model.ParamMatch{
-                                               Key:   &param.Key,
-                                               Value: 
model.StringMatchToModelStringMatch(param.Value),
-                                       })
-                               }
-
-                               resp.FlowWeightSets = 
append(resp.FlowWeightSets, model.FlowWeightSet{
-                                       Weight: int32(weight),
-                                       Scope:  scope,
-                               })
-                       }
-                       return false
-               })
+               resp, err := service.GetAppFlowWeight(ctx, appName, mesh)
                if err != nil {
-                       c.JSON(http.StatusInternalServerError, 
model.NewErrorResp(err.Error()))
+                       util.HandleServiceError(c, err)
                        return
                }
                c.JSON(http.StatusOK, model.NewSuccessResp(resp))
        }
 }
 
-func isFlowWeight(conf *meshproto.OverrideConfig) bool {
-       if conf.Side != consts.SideProvider ||
-               conf.Parameters == nil ||
-               conf.Match == nil ||
-               conf.Match.Param == nil {
-               return false
-       } else if _, ok := conf.Parameters[`weight`]; !ok {
-               return false
-       }
-       return true
-}
-
 func ApplicationConfigFlowWeightPUT(ctx consolectx.Context) gin.HandlerFunc {
        return func(c *gin.Context) {
-               var (
-                       appName string
-                       mesh    string
-                       body    = struct {
-                               FlowWeightSets []model.FlowWeightSet 
`json:"flowWeightSets"`
-                       }{}
-               )
-               appName = c.Query("appName")
-               mesh = c.Query("mesh")
+               body := struct {
+                       FlowWeightSets []model.FlowWeightSet 
`json:"flowWeightSets"`
+               }{}
+               appName := c.Query("appName")
+               mesh := c.Query("mesh")
                if strutil.IsBlank(appName) {
-                       c.JSON(http.StatusBadRequest, 
model.NewErrorResp("application name is required"))
+                       util.HandleArgumentError(c, errors.New("appName is 
required"))
+                       return
                }
                if strutil.IsBlank(mesh) {
-                       c.JSON(http.StatusBadRequest, model.NewErrorResp("mesh 
is required"))
+                       util.HandleArgumentError(c, errors.New("mesh is 
required"))
                        return
                }
                if err := c.Bind(&body); err != nil {
-                       c.JSON(http.StatusBadRequest, 
model.NewErrorResp(err.Error()))
+                       util.HandleArgumentError(c, err)
                        return
                }
-               // get from store, or generate default resource
-               isNotExist := false
-               appConfiguratorName := appName + consts.ConfiguratorRuleSuffix
-               res, err := service.GetConfigurator(ctx, appConfiguratorName, 
mesh)
+               err := service.UpInsertAppFlowWeightConfig(ctx, appName, mesh, 
body.FlowWeightSets)
                if err != nil {
-                       if corestore.IsResourceNotFound(err) {
-                               // for check app exist
-                               data, err := service.GetApplicationDetail(ctx, 
&model.ApplicationDetailReq{AppName: appName})
-                               if err != nil {
-                                       c.JSON(http.StatusNotFound, 
model.NewErrorResp(err.Error()))
-                                       return
-                               } else if data == nil {
-                                       c.JSON(http.StatusNotFound, 
model.NewErrorResp("application not found"))
-                                       return
-                               }
-                               res = 
meshresource.NewDynamicConfigResourceWithAttributes(appConfiguratorName, mesh)
-                               res.Spec = &meshproto.DynamicConfig{
-                                       Key:           appName,
-                                       Scope:         consts.ScopeApplication,
-                                       ConfigVersion: 
consts.ConfiguratorVersionV3,
-                                       Enabled:       true,
-                                       Configs:       
make([]*meshproto.OverrideConfig, 0),
-                               }
-                               isNotExist = true
-                       } else {
-                               c.JSON(http.StatusInternalServerError, 
model.NewErrorResp(err.Error()))
-                               return
-                       }
-               }
-
-               // remove old
-               res.Spec.RangeConfigsToRemove(func(conf 
*meshproto.OverrideConfig) (IsRemove bool) {
-                       return isFlowWeight(conf)
-               })
-               // append new
-               for _, set := range body.FlowWeightSets {
-                       paramMatch := make([]*meshproto.ParamMatch, 0, 
len(set.Scope))
-                       for _, match := range set.Scope {
-                               paramMatch = append(paramMatch, 
&meshproto.ParamMatch{
-                                       Key:   *match.Key,
-                                       Value: 
model.ModelStringMatchToStringMatch(match.Value),
-                               })
-                       }
-                       res.Spec.Configs = append(res.Spec.Configs, 
&meshproto.OverrideConfig{
-                               Side:       consts.SideProvider,
-                               Parameters: map[string]string{`weight`: 
strconv.Itoa(int(set.Weight))},
-                               Match: &meshproto.ConditionMatch{
-                                       Param: paramMatch,
-                               },
-                               XGenerateByCp: true,
-                       })
-               }
-               // restore
-               if isNotExist {
-                       err = service.CreateConfigurator(ctx, appName, res)
-                       if err != nil {
-                               c.JSON(http.StatusInternalServerError, 
model.NewErrorResp(err.Error()))
-                               return
-                       }
-               } else {
-                       err = service.UpdateConfigurator(ctx, appName, res)
-                       if err != nil {
-                               c.JSON(http.StatusInternalServerError, 
model.NewErrorResp(err.Error()))
-                               return
-                       }
+                       util.HandleServiceError(c, err)
+                       return
                }
-               c.JSON(http.StatusOK, model.NewSuccessResp(nil))
+               c.JSON(http.StatusOK, model.NewSuccessResp(true))
        }
 }
 
 func ApplicationConfigGrayGET(ctx consolectx.Context) gin.HandlerFunc {
        return func(c *gin.Context) {
-               var (
-                       appName string
-                       mesh    string
-                       resp    = struct {
-                               GraySets []model.GraySet `json:"graySets"`
-                       }{}
-               )
-               appName = c.Query("appName")
-               mesh = c.Query("mesh")
+               appName := c.Query("appName")
+               mesh := c.Query("mesh")
                if strutil.IsBlank(appName) {
-                       c.JSON(http.StatusBadRequest, 
model.NewErrorResp("appName is required"))
+                       util.HandleArgumentError(c, errors.New("appName is 
required"))
                        return
                }
                if strutil.IsBlank(mesh) {
-                       c.JSON(http.StatusBadRequest, model.NewErrorResp("mesh 
is required"))
+                       util.HandleArgumentError(c, errors.New("mesh is 
required"))
                        return
                }
-               serviceTagRuleName := appName + consts.TagRuleSuffix
-               res, err := service.GetTagRule(ctx, serviceTagRuleName, mesh)
+               resp, err := service.GetGrayConfig(ctx, appName, mesh)
                if err != nil {
-                       if corestore.IsResourceNotFound(err) {
-                               resp.GraySets = make([]model.GraySet, 0)
-                               c.JSON(http.StatusOK, 
model.NewSuccessResp(resp))
-                               return
-                       }
-                       c.JSON(http.StatusNotFound, 
model.NewErrorResp(err.Error()))
+                       util.HandleServiceError(c, err)
                        return
                }
-               resp.GraySets = make([]model.GraySet, 0, len(res.Spec.Tags))
-
-               res.Spec.RangeTags(func(tag *meshproto.Tag) (isStop bool) {
-                       if isGrayTag(tag) {
-                               scope := make([]model.ParamMatch, 0, 
len(tag.Match))
-                               for _, paramMatch := range tag.Match {
-                                       scope = append(scope, model.ParamMatch{
-                                               Key:   &paramMatch.Key,
-                                               Value: 
model.StringMatchToModelStringMatch(paramMatch.Value),
-                                       })
-                               }
-                               resp.GraySets = append(resp.GraySets, 
model.GraySet{
-                                       EnvName: tag.Name,
-                                       Scope:   scope,
-                               })
-                       }
-                       return false
-               })
-
                c.JSON(http.StatusOK, model.NewSuccessResp(resp))
        }
 }
 
 func ApplicationConfigGrayPUT(ctx consolectx.Context) gin.HandlerFunc {
        return func(c *gin.Context) {
-               var (
-                       appName string
-                       mesh    string
-                       body    = struct {
-                               GraySets []model.GraySet `json:"graySets"`
-                       }{}
-               )
-               appName = c.Query("appName")
-               mesh = c.Query("mesh")
+               body := struct {
+                       GraySets []model.GraySet `json:"graySets"`
+               }{}
+               appName := c.Query("appName")
+               mesh := c.Query("mesh")
                if strutil.IsBlank(appName) {
-                       c.JSON(http.StatusBadRequest, 
model.NewErrorResp("application name is required"))
+                       util.HandleArgumentError(c, errors.New("appName is 
required"))
                        return
                }
                if strutil.IsBlank(mesh) {
-                       c.JSON(http.StatusBadRequest, model.NewErrorResp("mesh 
is required"))
+                       util.HandleArgumentError(c, errors.New("mesh is 
required"))
                        return
                }
                if err := c.Bind(&body); err != nil {
-                       c.JSON(http.StatusBadRequest, 
model.NewErrorResp(err.Error()))
+                       util.HandleArgumentError(c, err)
+                       return
                }
-
-               isNotExist := false
-               serviceTagRuleName := appName + consts.TagRuleSuffix
-               res, err := service.GetTagRule(ctx, serviceTagRuleName, mesh)
+               err := service.UpInsertAppGrayConfig(ctx, appName, mesh, 
body.GraySets)
                if err != nil {
-                       c.JSON(http.StatusInternalServerError, 
model.NewErrorResp(err.Error()))
-               }
-               if res == nil {
-                       data, err := service.GetApplicationDetail(ctx, 
&model.ApplicationDetailReq{AppName: appName, Mesh: mesh})
-                       if err != nil {
-                               c.JSON(http.StatusNotFound, 
model.NewErrorResp(err.Error()))
-                               return
-                       } else if data == nil {
-                               c.JSON(http.StatusNotFound, 
model.NewErrorResp("application not found"))
-                               return
-                       }
-
-                       res = 
meshresource.NewTagRouteResourceWithAttributes(serviceTagRuleName, mesh)
-                       res.Spec = &meshproto.TagRoute{
-                               Enabled:       true,
-                               Key:           appName,
-                               ConfigVersion: consts.ConfiguratorVersionV3,
-                               Force:         false,
-                               Tags:          make([]*meshproto.Tag, 0),
-                       }
-                       isNotExist = true
-               }
-
-               // remove old config, generate config from admin, append
-               res.Spec.RangeTagsToRemove(func(tag *meshproto.Tag) (IsRemove 
bool) {
-                       return isGrayTag(tag)
-               })
-               newTags := make([]*meshproto.Tag, 0)
-               for _, set := range body.GraySets {
-                       paramMatches := make([]*meshproto.ParamMatch, 0, 
len(set.Scope))
-                       for _, match := range set.Scope {
-                               paramMatches = append(paramMatches, 
&meshproto.ParamMatch{
-                                       Key:   *match.Key,
-                                       Value: 
model.ModelStringMatchToStringMatch(match.Value),
-                               })
-                       }
-                       newTags = append(newTags, &meshproto.Tag{
-                               Name:          set.EnvName,
-                               Match:         paramMatches,
-                               XGenerateByCp: true,
-                       })
-               }
-               res.Spec.Tags = append(res.Spec.Tags, newTags...)
-
-               // restore
-               if isNotExist {
-                       err = service.CreateTagRule(ctx, res)
-                       if err != nil {
-                               c.JSON(http.StatusInternalServerError, 
model.NewErrorResp(err.Error()))
-                               return
-                       }
-               } else {
-                       err = service.UpdateTagRule(ctx, res)
-                       if err != nil {
-                               c.JSON(http.StatusInternalServerError, 
model.NewErrorResp(err.Error()))
-                               return
-                       }
+                       util.HandleServiceError(c, err)
+                       return
                }
-               c.JSON(http.StatusOK, model.NewSuccessResp(nil))
-       }
-}
-
-func isGrayTag(tag *meshproto.Tag) bool {
-       if tag.Name == "" || tag.Addresses != nil || len(tag.Addresses) != 0 {
-               return false
+               c.JSON(http.StatusOK, model.NewSuccessResp(true))
        }
-       return true
 }
diff --git a/pkg/console/handler/auth.go b/pkg/console/handler/auth.go
index dc1777e4..0ec4dcf7 100644
--- a/pkg/console/handler/auth.go
+++ b/pkg/console/handler/auth.go
@@ -23,6 +23,7 @@ import (
        "github.com/gin-contrib/sessions"
        "github.com/gin-gonic/gin"
 
+       "github.com/apache/dubbo-admin/pkg/common/bizerror"
        consolectx "github.com/apache/dubbo-admin/pkg/console/context"
        "github.com/apache/dubbo-admin/pkg/console/model"
 )
@@ -33,21 +34,24 @@ func Login(ctx consolectx.Context) gin.HandlerFunc {
                password := c.PostForm("password")
                // verify username and password
                authCfg := ctx.Config().Console.Auth
-               if user == authCfg.User && password == authCfg.Password {
-                       session := sessions.Default(c)
-                       session.Set("user", user)
-                       session.Options(sessions.Options{
-                               MaxAge: authCfg.ExpirationTime,
-                               Path:   "/",
-                       })
-                       err := session.Save()
-                       if err != nil {
-                               c.JSON(http.StatusInternalServerError, 
model.NewErrorResp(err.Error()))
-                       }
-                       c.JSON(http.StatusOK, model.NewSuccessResp(nil))
-               } else {
-                       c.JSON(http.StatusUnauthorized, 
model.NewUnauthorizedResp())
+               if user != authCfg.User || password != authCfg.Password {
+                       authErr := bizerror.NewBizError(bizerror.Unauthorized, 
"username or password is not correct!")
+                       c.JSON(http.StatusUnauthorized, 
model.NewBizErrorResp(authErr))
+                       return
                }
+               session := sessions.Default(c)
+               session.Set("user", user)
+               session.Options(sessions.Options{
+                       MaxAge: authCfg.ExpirationTime,
+                       Path:   "/",
+               })
+               err := session.Save()
+               if err != nil {
+                       sessionErr := 
bizerror.NewBizError(bizerror.SessionError, err.Error())
+                       c.JSON(http.StatusOK, model.NewBizErrorResp(sessionErr))
+                       return
+               }
+               c.JSON(http.StatusOK, model.NewSuccessResp(true))
        }
 }
 
@@ -57,8 +61,10 @@ func Logout(_ consolectx.Context) gin.HandlerFunc {
                session.Clear()
                err := session.Save()
                if err != nil {
-                       c.JSON(http.StatusInternalServerError, 
model.NewErrorResp(err.Error()))
+                       sessionErr := 
bizerror.NewBizError(bizerror.SessionError, err.Error())
+                       c.JSON(http.StatusOK, model.NewBizErrorResp(sessionErr))
+                       return
                }
-               c.JSON(http.StatusOK, model.NewSuccessResp(nil))
+               c.JSON(http.StatusOK, model.NewSuccessResp(true))
        }
 }
diff --git a/pkg/common/bizerror/common.go b/pkg/console/handler/mesh.go
similarity index 53%
copy from pkg/common/bizerror/common.go
copy to pkg/console/handler/mesh.go
index 1f993c4b..3ebee0f9 100644
--- a/pkg/common/bizerror/common.go
+++ b/pkg/console/handler/mesh.go
@@ -15,41 +15,29 @@
  * limitations under the License.
  */
 
-package bizerror
+package handler
 
 import (
-       "errors"
-       "fmt"
-)
-
-type AssertionError struct {
-       msg string
-}
-
-func NewAssertionError(expected, actual interface{}) error {
-       return &AssertionError{
-               msg: fmt.Sprintf("type assertion error, expected:%v, 
actual:%v", expected, actual),
-       }
-}
-
-func (e *AssertionError) Error() string {
-       return e.msg
-}
-
-type MeshNotFoundError struct {
-       Mesh string
-}
+       "net/http"
 
-func (m *MeshNotFoundError) Error() string {
-       return fmt.Sprintf("mesh of name %s is not found", m.Mesh)
-}
+       "github.com/duke-git/lancet/v2/slice"
+       "github.com/gin-gonic/gin"
 
-func MeshNotFound(meshName string) error {
-       return &MeshNotFoundError{meshName}
-}
+       discoverycfg "github.com/apache/dubbo-admin/pkg/config/discovery"
+       consolectx "github.com/apache/dubbo-admin/pkg/console/context"
+       "github.com/apache/dubbo-admin/pkg/console/model"
+)
 
-func IsMeshNotFound(err error) bool {
-       var meshNotFoundError *MeshNotFoundError
-       ok := errors.As(err, &meshNotFoundError)
-       return ok
+// ListMeshes list all meshes(discoveries) defined in config
+func ListMeshes(ctx consolectx.Context) gin.HandlerFunc {
+       return func(c *gin.Context) {
+               discoveries := ctx.Config().Discovery
+               meshes := slice.Map(discoveries, func(index int, item 
*discoverycfg.Config) model.MeshResp {
+                       return model.MeshResp{
+                               Name: item.Name,
+                               Type: string(item.Type),
+                       }
+               })
+               c.JSON(http.StatusOK, model.NewSuccessResp(meshes))
+       }
 }
diff --git a/pkg/console/model/application.go b/pkg/console/model/application.go
index 3720de36..5b02b674 100644
--- a/pkg/console/model/application.go
+++ b/pkg/console/model/application.go
@@ -195,3 +195,15 @@ type GraySet struct {
        EnvName string       `json:"name,omitempty"`
        Scope   []ParamMatch `json:"scope,omitempty"`
 }
+
+type AppAccessLogConfigResp struct {
+       AccessLog bool `json:"operatorLog"`
+}
+
+type AppFlowWeightConfigResp struct {
+       FlowWeightSets []FlowWeightSet `json:"flowWeightSets"`
+}
+
+type AppGrayConfigResp struct {
+       GraySets []GraySet `json:"graySets"`
+}
diff --git a/pkg/console/model/common.go b/pkg/console/model/common.go
index 7a11ea86..18ab50b3 100644
--- a/pkg/console/model/common.go
+++ b/pkg/console/model/common.go
@@ -17,21 +17,18 @@
 
 package model
 
-import coremodel "github.com/apache/dubbo-admin/pkg/core/resource/model"
-
-const (
-       successCode      = 200
-       unauthorizedCode = 401
-       errorCode        = 500
+import (
+       "github.com/apache/dubbo-admin/pkg/common/bizerror"
+       coremodel "github.com/apache/dubbo-admin/pkg/core/resource/model"
 )
 
 type CommonResp struct {
-       Code int    `json:"code"`
+       Code string `json:"code"`
        Msg  string `json:"msg"`
        Data any    `json:"data"`
 }
 
-func (r *CommonResp) WithCode(code int) *CommonResp {
+func (r *CommonResp) WithCode(code string) *CommonResp {
        r.Code = code
        return r
 }
@@ -48,24 +45,25 @@ func (r *CommonResp) WithData(data any) *CommonResp {
 
 func NewSuccessResp(data any) *CommonResp {
        return &CommonResp{
-               Code: successCode,
+               Code: "Success",
                Msg:  "success",
                Data: data,
        }
 }
 
-func NewUnauthorizedResp() *CommonResp {
+// NewErrorResp TODO replace with NewBizErrorResp
+func NewErrorResp(msg string) *CommonResp {
        return &CommonResp{
-               Code: unauthorizedCode,
-               Msg:  "UnAuthorized, please login",
+               Code: string(bizerror.UnknownError),
+               Msg:  msg,
                Data: nil,
        }
 }
 
-func NewErrorResp(msg string) *CommonResp {
+func NewBizErrorResp(err bizerror.Error) *CommonResp {
        return &CommonResp{
-               Code: errorCode,
-               Msg:  msg,
+               Code: string(err.Code()),
+               Msg:  err.Message(),
                Data: nil,
        }
 }
diff --git a/pkg/console/model/condition_rule.go 
b/pkg/console/model/condition_rule.go
index e13d7e2c..42bb6bc6 100644
--- a/pkg/console/model/condition_rule.go
+++ b/pkg/console/model/condition_rule.go
@@ -18,7 +18,6 @@
 package model
 
 import (
-       "net/http"
        "strings"
 
        meshproto "github.com/apache/dubbo-admin/api/mesh/v1alpha1"
@@ -209,11 +208,7 @@ func matchValueToDestinationCondition(val string) 
[]DestinationCondition {
 
 func GenConditionRuleToResp(data *meshproto.ConditionRoute) *CommonResp {
        if data == nil {
-               return &CommonResp{
-                       Code: http.StatusNotFound,
-                       Msg:  "not found",
-                       Data: map[string]string{},
-               }
+               return NewSuccessResp(nil)
        }
        if pb := data.ToConditionRouteV3(); pb != nil {
                return NewSuccessResp(ConditionRuleResp{
@@ -249,11 +244,7 @@ func GenConditionRuleToResp(data 
*meshproto.ConditionRoute) *CommonResp {
                }
                return NewSuccessResp(res)
        } else {
-               return &CommonResp{
-                       Code: http.StatusInternalServerError,
-                       Msg:  "invalid condition rule",
-                       Data: data,
-               }
+               return NewErrorResp("invalid condition rule")
        }
 }
 
diff --git a/pkg/console/model/configurator_rule.go 
b/pkg/console/model/configurator_rule.go
index facced62..677f742e 100644
--- a/pkg/console/model/configurator_rule.go
+++ b/pkg/console/model/configurator_rule.go
@@ -18,8 +18,6 @@
 package model
 
 import (
-       "net/http"
-
        meshproto "github.com/apache/dubbo-admin/api/mesh/v1alpha1"
        coremodel "github.com/apache/dubbo-admin/pkg/core/resource/model"
 )
@@ -101,7 +99,7 @@ type RespAddressMatch struct {
 }
 
 func GenDynamicConfigToResp(pb *meshproto.DynamicConfig) (res *CommonResp) {
-       cfg := RespConfigurator{}
+       cfg := &RespConfigurator{}
        if pb != nil {
                cfg.ConfigVersion = pb.ConfigVersion
                cfg.Key = pb.Key
@@ -110,10 +108,7 @@ func GenDynamicConfigToResp(pb *meshproto.DynamicConfig) 
(res *CommonResp) {
                cfg.Configs = overrideConfigToRespConfigItem(pb.Configs)
                return NewSuccessResp(cfg)
        }
-       return &CommonResp{
-               Code: http.StatusNotFound,
-               Msg:  "configurator not found",
-       }
+       return NewSuccessResp(nil)
 }
 
 func overrideConfigToRespConfigItem(OverrideConfigs 
[]*meshproto.OverrideConfig) []ConfigItem {
diff --git a/pkg/common/bizerror/common.go b/pkg/console/model/mesh.go
similarity index 53%
copy from pkg/common/bizerror/common.go
copy to pkg/console/model/mesh.go
index 1f993c4b..7ee9e538 100644
--- a/pkg/common/bizerror/common.go
+++ b/pkg/console/model/mesh.go
@@ -15,41 +15,9 @@
  * limitations under the License.
  */
 
-package bizerror
+package model
 
-import (
-       "errors"
-       "fmt"
-)
-
-type AssertionError struct {
-       msg string
-}
-
-func NewAssertionError(expected, actual interface{}) error {
-       return &AssertionError{
-               msg: fmt.Sprintf("type assertion error, expected:%v, 
actual:%v", expected, actual),
-       }
-}
-
-func (e *AssertionError) Error() string {
-       return e.msg
-}
-
-type MeshNotFoundError struct {
-       Mesh string
-}
-
-func (m *MeshNotFoundError) Error() string {
-       return fmt.Sprintf("mesh of name %s is not found", m.Mesh)
-}
-
-func MeshNotFound(meshName string) error {
-       return &MeshNotFoundError{meshName}
-}
-
-func IsMeshNotFound(err error) bool {
-       var meshNotFoundError *MeshNotFoundError
-       ok := errors.As(err, &meshNotFoundError)
-       return ok
+type MeshResp struct {
+       Name string `json:"name"`
+       Type string `json:"type"`
 }
diff --git a/pkg/console/model/tag_rule.go b/pkg/console/model/tag_rule.go
index 7bdf1ecd..6890ef1f 100644
--- a/pkg/console/model/tag_rule.go
+++ b/pkg/console/model/tag_rule.go
@@ -18,8 +18,6 @@
 package model
 
 import (
-       "net/http"
-
        meshproto "github.com/apache/dubbo-admin/api/mesh/v1alpha1"
        "github.com/apache/dubbo-admin/pkg/core/consts"
 )
@@ -47,11 +45,7 @@ type RespTagElement struct {
 
 func GenTagRouteResp(pb *meshproto.TagRoute) *CommonResp {
        if pb == nil {
-               return &CommonResp{
-                       Code: http.StatusNotFound,
-                       Msg:  "tag rule not found",
-                       Data: "",
-               }
+               return NewSuccessResp(nil)
        } else {
                return NewSuccessResp(TagRuleResp{
                        ConfigVersion: pb.ConfigVersion,
diff --git a/pkg/console/router/router.go b/pkg/console/router/router.go
index 334f9fee..f8370699 100644
--- a/pkg/console/router/router.go
+++ b/pkg/console/router/router.go
@@ -67,8 +67,8 @@ func InitRouter(r *gin.Engine, ctx consolectx.Context) {
                application.GET("/search", handler.ApplicationSearch(ctx))
                {
                        applicationConfig := application.Group("/config")
-                       applicationConfig.PUT("/operatorLog", 
handler.ApplicationConfigOperatorLogPut(ctx))
-                       applicationConfig.GET("/operatorLog", 
handler.ApplicationConfigOperatorLogGet(ctx))
+                       applicationConfig.PUT("/operatorLog", 
handler.ApplicationConfigAccessLogPut(ctx))
+                       applicationConfig.GET("/operatorLog", 
handler.ApplicationConfigAccessLogGet(ctx))
 
                        applicationConfig.GET("/flowWeight", 
handler.ApplicationConfigFlowWeightGET(ctx))
                        applicationConfig.PUT("/flowWeight", 
handler.ApplicationConfigFlowWeightPUT(ctx))
@@ -139,4 +139,5 @@ func InitRouter(r *gin.Engine, ctx consolectx.Context) {
        router.GET("/search", handler.BannerGlobalSearch(ctx))
        router.GET("/overview", handler.ClusterOverview(ctx))
        router.GET("/metadata", handler.AdminMetadata(ctx))
+       router.GET("/meshes", handler.ListMeshes(ctx))
 }
diff --git a/pkg/console/service/application.go 
b/pkg/console/service/application.go
index d78f512a..836c7019 100644
--- a/pkg/console/service/application.go
+++ b/pkg/console/service/application.go
@@ -18,13 +18,19 @@
 package service
 
 import (
+       "fmt"
+       "strconv"
+
        "github.com/duke-git/lancet/v2/maputil"
        "github.com/duke-git/lancet/v2/slice"
        "github.com/duke-git/lancet/v2/strutil"
 
+       meshproto "github.com/apache/dubbo-admin/api/mesh/v1alpha1"
+       "github.com/apache/dubbo-admin/pkg/common/bizerror"
        consolectx "github.com/apache/dubbo-admin/pkg/console/context"
        "github.com/apache/dubbo-admin/pkg/console/model"
        "github.com/apache/dubbo-admin/pkg/core/consts"
+       "github.com/apache/dubbo-admin/pkg/core/logger"
        "github.com/apache/dubbo-admin/pkg/core/manager"
        meshresource 
"github.com/apache/dubbo-admin/pkg/core/resource/apis/mesh/v1alpha1"
        coremodel "github.com/apache/dubbo-admin/pkg/core/resource/model"
@@ -267,3 +273,405 @@ func buildApplicationSearchResp(appResource 
*meshresource.ApplicationResource, m
                RegistryClusters: []string{mesh},
        }
 }
+
+func isAppAccessLogConfig(conf *meshproto.OverrideConfig, appName string) bool 
{
+       if conf.Side != consts.SideProvider ||
+               conf.Parameters == nil ||
+               conf.Match == nil ||
+               conf.Match.Application == nil ||
+               conf.Match.Application.Oneof == nil ||
+               len(conf.Match.Application.Oneof) != 1 ||
+               conf.Match.Application.Oneof[0].Exact != appName {
+               return false
+       }
+       if _, ok := conf.Parameters[`accesslog`]; !ok {
+               return false
+       }
+       return true
+}
+
+func UpInsertAppAccessLog(ctx consolectx.Context, appName string, 
openAccessLog bool, mesh string) error {
+       // check app exists
+       data, err := GetApplicationDetail(ctx, 
&model.ApplicationDetailReq{AppName: appName})
+       if err != nil {
+               return err
+       }
+       if data == nil {
+               return bizerror.NewBizError(bizerror.AppNotFound, 
fmt.Sprintf("%s does not exist", appName))
+       }
+       // check app configurator exists
+       appConfiguratorName := appName + consts.ConfiguratorRuleSuffix
+       res, err := GetConfigurator(ctx, appConfiguratorName, mesh)
+       if err != nil {
+               return err
+       }
+       // if not exists, create one configurator with access log enable
+       if res == nil {
+               return insertConfiguratorWithAccessLog(ctx, res, openAccessLog, 
appConfiguratorName, appName, mesh)
+       }
+       // else we update the configurator
+       return updateConfiguratorWithAccessLog(ctx, res, openAccessLog, 
appConfiguratorName, appName, mesh)
+}
+
+func insertConfiguratorWithAccessLog(ctx consolectx.Context, res 
*meshresource.DynamicConfigResource, openAccessLog bool,
+       appConfiguratorName, appName, mesh string) error {
+       // configurator is nil, accessLog is already closed
+       if !openAccessLog {
+               return nil
+       }
+       res = 
meshresource.NewDynamicConfigResourceWithAttributes(appConfiguratorName, mesh)
+       res.Spec = &meshproto.DynamicConfig{
+               Key:           appName,
+               Scope:         consts.ScopeApplication,
+               ConfigVersion: consts.ConfiguratorVersionV3,
+               Enabled:       true,
+               Configs:       make([]*meshproto.OverrideConfig, 0),
+       }
+       res.Spec.Configs = append(res.Spec.Configs, 
newAccessLogEnabledConfig(appName))
+       err := CreateConfigurator(ctx, appConfiguratorName, res)
+       if err != nil {
+               logger.Errorf("create configurator failed when open accesslog, 
resourceKey: %s, openAccessLog: %t, err: %s",
+                       coremodel.BuildResourceKey(mesh, appName), 
openAccessLog, err)
+               return err
+       }
+       return nil
+}
+
+func updateConfiguratorWithAccessLog(ctx consolectx.Context, res 
*meshresource.DynamicConfigResource, openAccessLog bool,
+       appConfiguratorName, appName, mesh string) error {
+       var accessLogConfig *meshproto.OverrideConfig
+       res.Spec.RangeConfig(func(conf *meshproto.OverrideConfig) (isStop bool) 
{
+               if isAppAccessLogConfig(conf, appName) {
+                       accessLogConfig = conf
+                       return true
+               }
+               return false
+       })
+       // access log config not found
+       if accessLogConfig == nil {
+               // access log needs to be closed and already closed
+               if !openAccessLog {
+                       return nil
+               }
+               // insert a access log enabled config
+               res.Spec.Configs = append(res.Spec.Configs, 
newAccessLogEnabledConfig(appName))
+       } else {
+               // access log config found and status is the same as needed
+               if accessLogConfig.Enabled == openAccessLog {
+                       return nil
+               }
+               // update the access log enabled status as needed
+               accessLogConfig.Enabled = openAccessLog
+       }
+       err := UpdateConfigurator(ctx, appConfiguratorName, res)
+       if err != nil {
+               logger.Errorf("update configurator failed when opening 
accesslog, resourceKey: %s, openAccessLog: %t, err: %s",
+                       coremodel.BuildResourceKey(mesh, appName), 
openAccessLog, err)
+               return err
+       }
+       return nil
+}
+
+func newAccessLogEnabledConfig(appName string) *meshproto.OverrideConfig {
+       return &meshproto.OverrideConfig{
+               Side:       consts.SideProvider,
+               Parameters: map[string]string{`accesslog`: `true`},
+               Enabled:    true,
+               Match: &meshproto.ConditionMatch{
+                       Application: &meshproto.ListStringMatch{
+                               Oneof: []*meshproto.StringMatch{
+                                       {
+                                               Exact: appName,
+                                       },
+                               }}},
+               XGenerateByCp: true,
+       }
+}
+
+func GetAppAccessLog(ctx consolectx.Context, appName string, mesh string) 
(*model.AppAccessLogConfigResp, error) {
+       appConfiguratorName := appName + consts.ConfiguratorRuleSuffix
+       res, err := GetConfigurator(ctx, appConfiguratorName, mesh)
+       resp := &model.AppAccessLogConfigResp{
+               AccessLog: false,
+       }
+       if err != nil {
+               logger.Errorf("get configurator failed when get app accesslog, 
resourceKey: %s, err: %s",
+                       coremodel.BuildResourceKey(mesh, appName), err)
+               return nil, err
+       }
+       if res == nil {
+               return resp, nil
+       }
+       var appAccessLogConfig *meshproto.OverrideConfig
+       res.Spec.RangeConfig(func(conf *meshproto.OverrideConfig) (isStop bool) 
{
+               if isAppAccessLogConfig(conf, appName) {
+                       appAccessLogConfig = conf
+                       return true
+               }
+               return false
+       })
+       if appAccessLogConfig == nil {
+               return resp, nil
+       }
+       resp.AccessLog = appAccessLogConfig.Enabled
+       return resp, nil
+}
+
+func GetAppFlowWeight(ctx consolectx.Context, appName string, mesh string) 
(*model.AppFlowWeightConfigResp, error) {
+       resp := &model.AppFlowWeightConfigResp{
+               FlowWeightSets: []model.FlowWeightSet{},
+       }
+       appConfiguratorName := appName + consts.ConfiguratorRuleSuffix
+       res, err := GetConfigurator(ctx, appConfiguratorName, mesh)
+       if err != nil {
+               logger.Errorf("get configurator failed when get app flow 
weight, resourceKey: %s, err: %s",
+                       coremodel.BuildResourceKey(mesh, appName), err)
+               return nil, err
+       }
+       if res == nil {
+               return resp, nil
+       }
+
+       weight := 0
+       res.Spec.RangeConfig(func(conf *meshproto.OverrideConfig) (isStop bool) 
{
+               if isFlowWeightConfig(conf) {
+                       weight, err = strconv.Atoi(conf.Parameters[`weight`])
+                       if err != nil {
+                               logger.Error("parse weight failed", err)
+                               return true
+                       }
+                       scope := make([]model.ParamMatch, 0, 
len(conf.Match.Param))
+                       for _, param := range conf.Match.Param {
+                               scope = append(scope, model.ParamMatch{
+                                       Key:   &param.Key,
+                                       Value: 
model.StringMatchToModelStringMatch(param.Value),
+                               })
+                       }
+
+                       resp.FlowWeightSets = append(resp.FlowWeightSets, 
model.FlowWeightSet{
+                               Weight: int32(weight),
+                               Scope:  scope,
+                       })
+               }
+               return false
+       })
+       return resp, nil
+}
+
+func isFlowWeightConfig(conf *meshproto.OverrideConfig) bool {
+       if conf.Side != consts.SideProvider ||
+               conf.Parameters == nil ||
+               conf.Match == nil ||
+               conf.Match.Param == nil {
+               return false
+       }
+       if _, ok := conf.Parameters[`weight`]; !ok {
+               return false
+       }
+       return true
+}
+
+func UpInsertAppFlowWeightConfig(ctx consolectx.Context, appName string, mesh 
string, flowWeightSets []model.FlowWeightSet) error {
+       // check app exists
+       data, err := GetApplicationDetail(ctx, 
&model.ApplicationDetailReq{AppName: appName})
+       if err != nil {
+               return err
+       }
+       if data == nil {
+               return bizerror.NewBizError(bizerror.AppNotFound, 
fmt.Sprintf("%s does not exist", appName))
+       }
+       appConfiguratorName := appName + consts.ConfiguratorRuleSuffix
+       res, err := GetConfigurator(ctx, appConfiguratorName, mesh)
+       if err != nil {
+               logger.Errorf("get configurator failed when update app flow 
weight, resourceKey: %s, err: %s",
+                       coremodel.BuildResourceKey(mesh, appName), err)
+               return err
+       }
+       // configurator not exists, insert a new one
+       if res == nil {
+               return insertConfiguratorWithFlowWeight(ctx, flowWeightSets, 
appName, appConfiguratorName, mesh)
+       }
+       // configurator exists, update it
+
+       // remove old flow weight config
+       res.Spec.RangeConfigsToRemove(func(conf *meshproto.OverrideConfig) 
(IsRemove bool) {
+               return isFlowWeightConfig(conf)
+       })
+
+       // add new flow weight config
+       flowWeightConfigs := slice.Map(flowWeightSets, func(index int, set 
model.FlowWeightSet) *meshproto.OverrideConfig {
+               return fromFlowWeightSet(set)
+       })
+       res.Spec.Configs = slice.Union(res.Spec.Configs, flowWeightConfigs)
+
+       err = UpdateConfigurator(ctx, appConfiguratorName, res)
+       if err != nil {
+               logger.Errorf("update configurator failed with app flow weight, 
resourceKey: %s, err: %s",
+                       coremodel.BuildResourceKey(mesh, appName), err)
+               return err
+       }
+       return nil
+}
+
+func insertConfiguratorWithFlowWeight(
+       ctx consolectx.Context,
+       flowWeightSets []model.FlowWeightSet,
+       appName, appConfiguratorName, mesh string) error {
+       res := 
meshresource.NewDynamicConfigResourceWithAttributes(appConfiguratorName, mesh)
+       res.Spec = &meshproto.DynamicConfig{
+               Key:           appName,
+               Scope:         consts.ScopeApplication,
+               ConfigVersion: consts.ConfiguratorVersionV3,
+               Enabled:       true,
+       }
+       flowWeightConfigs := slice.Map(flowWeightSets, func(index int, set 
model.FlowWeightSet) *meshproto.OverrideConfig {
+               return fromFlowWeightSet(set)
+       })
+       res.Spec.Configs = flowWeightConfigs
+       err := CreateConfigurator(ctx, appConfiguratorName, res)
+       if err != nil {
+               logger.Errorf("insert configurator failed with app flow weight, 
resourceKey: %s, err: %s",
+                       coremodel.BuildResourceKey(mesh, appName), err)
+               return err
+       }
+       return nil
+}
+
+func fromFlowWeightSet(set model.FlowWeightSet) *meshproto.OverrideConfig {
+       paramMatch := make([]*meshproto.ParamMatch, 0, len(set.Scope))
+       for _, match := range set.Scope {
+               paramMatch = append(paramMatch, &meshproto.ParamMatch{
+                       Key:   *match.Key,
+                       Value: model.ModelStringMatchToStringMatch(match.Value),
+               })
+       }
+       return &meshproto.OverrideConfig{
+               Side:       consts.SideProvider,
+               Parameters: map[string]string{`weight`: 
strconv.Itoa(int(set.Weight))},
+               Match: &meshproto.ConditionMatch{
+                       Param: paramMatch,
+               },
+               XGenerateByCp: true,
+       }
+}
+func GetGrayConfig(ctx consolectx.Context, appName string, mesh string) 
(*model.AppGrayConfigResp, error) {
+       resp := &model.AppGrayConfigResp{}
+       serviceTagRuleName := appName + consts.TagRuleSuffix
+       res, err := GetTagRule(ctx, serviceTagRuleName, mesh)
+       if err != nil {
+               logger.Errorf("get tag rule failed when get gray config, 
resourceKey: %s, err: %s",
+                       coremodel.BuildResourceKey(mesh, appName), err)
+               return nil, err
+       }
+       if res == nil {
+               return resp, err
+       }
+       resp.GraySets = make([]model.GraySet, 0, len(res.Spec.Tags))
+
+       res.Spec.RangeTags(func(tag *meshproto.Tag) (isStop bool) {
+               if isGrayTag(tag) {
+                       scope := make([]model.ParamMatch, 0, len(tag.Match))
+                       for _, paramMatch := range tag.Match {
+                               scope = append(scope, model.ParamMatch{
+                                       Key:   &paramMatch.Key,
+                                       Value: 
model.StringMatchToModelStringMatch(paramMatch.Value),
+                               })
+                       }
+                       resp.GraySets = append(resp.GraySets, model.GraySet{
+                               EnvName: tag.Name,
+                               Scope:   scope,
+                       })
+               }
+               return false
+       })
+       return resp, nil
+}
+
+func isGrayTag(tag *meshproto.Tag) bool {
+       if tag.Name == "" || len(tag.Addresses) != 0 {
+               return false
+       }
+       return true
+}
+
+func UpInsertAppGrayConfig(ctx consolectx.Context, appName string, mesh 
string, graySets []model.GraySet) error {
+       serviceTagRuleName := appName + consts.TagRuleSuffix
+       res, err := GetTagRule(ctx, serviceTagRuleName, mesh)
+       if err != nil {
+               logger.Errorf("get tag rule failed when update app gray config, 
resourceKey: %s, err: %s",
+                       coremodel.BuildResourceKey(mesh, appName), err)
+               return err
+       }
+       _, err = GetApplicationDetail(ctx, &model.ApplicationDetailReq{AppName: 
appName, Mesh: mesh})
+       if err != nil {
+               return err
+       }
+       // tag rule not exists, insert a new one
+       if res == nil {
+               return insertTagRuleWithGrayConfig(ctx, graySets, appName, 
serviceTagRuleName, mesh)
+       }
+       // tag rule exists, update it
+       return updateTagRuleWithGrayConfig(ctx, graySets, res, appName, mesh)
+}
+
+func insertTagRuleWithGrayConfig(
+       ctx consolectx.Context,
+       graySets []model.GraySet,
+       appName, serviceTagRuleName, mesh string) error {
+       res := 
meshresource.NewTagRouteResourceWithAttributes(serviceTagRuleName, mesh)
+       res.Spec = &meshproto.TagRoute{
+               Enabled:       true,
+               Key:           appName,
+               ConfigVersion: consts.ConfiguratorVersionV3,
+               Force:         false,
+       }
+       tags := slice.Map(graySets, func(index int, set model.GraySet) 
*meshproto.Tag {
+               return fromGraySet(set)
+       })
+       res.Spec.Tags = tags
+       err := CreateTagRule(ctx, res)
+       if err != nil {
+               logger.Errorf("insert tag rule failed with app gray config, 
resourceKey: %s, err: %s",
+                       coremodel.BuildResourceKey(mesh, appName), err)
+               return err
+       }
+       return nil
+}
+
+func updateTagRuleWithGrayConfig(
+       ctx consolectx.Context,
+       graySets []model.GraySet,
+       res *meshresource.TagRouteResource,
+       appName string, mesh string) error {
+       // remove old config, generate config from admin, append
+       res.Spec.RangeTagsToRemove(func(tag *meshproto.Tag) (IsRemove bool) {
+               return isGrayTag(tag)
+       })
+       tags := slice.Map(graySets, func(index int, set model.GraySet) 
*meshproto.Tag {
+               return fromGraySet(set)
+       })
+       res.Spec.Tags = append(res.Spec.Tags, tags...)
+       err := UpdateTagRule(ctx, res)
+       if err != nil {
+               logger.Errorf("update tag rule failed with app gray config, 
resourceKey: %s, err: %s",
+                       coremodel.BuildResourceKey(mesh, appName), err)
+               return err
+       }
+       return nil
+}
+
+func fromGraySet(set model.GraySet) *meshproto.Tag {
+       paramMatches := make([]*meshproto.ParamMatch, 0, len(set.Scope))
+       for _, match := range set.Scope {
+               paramMatches = append(paramMatches, &meshproto.ParamMatch{
+                       Key:   *match.Key,
+                       Value: model.ModelStringMatchToStringMatch(match.Value),
+               })
+       }
+
+       return &meshproto.Tag{
+               Name:          set.EnvName,
+               Match:         paramMatches,
+               XGenerateByCp: true,
+       }
+}
diff --git a/pkg/common/bizerror/common.go b/pkg/console/util/error.go
similarity index 55%
copy from pkg/common/bizerror/common.go
copy to pkg/console/util/error.go
index 1f993c4b..62eb3287 100644
--- a/pkg/common/bizerror/common.go
+++ b/pkg/console/util/error.go
@@ -15,41 +15,27 @@
  * limitations under the License.
  */
 
-package bizerror
+package util
 
 import (
        "errors"
-       "fmt"
-)
-
-type AssertionError struct {
-       msg string
-}
-
-func NewAssertionError(expected, actual interface{}) error {
-       return &AssertionError{
-               msg: fmt.Sprintf("type assertion error, expected:%v, 
actual:%v", expected, actual),
-       }
-}
+       "net/http"
 
-func (e *AssertionError) Error() string {
-       return e.msg
-}
-
-type MeshNotFoundError struct {
-       Mesh string
-}
+       "github.com/gin-gonic/gin"
 
-func (m *MeshNotFoundError) Error() string {
-       return fmt.Sprintf("mesh of name %s is not found", m.Mesh)
-}
+       "github.com/apache/dubbo-admin/pkg/common/bizerror"
+       "github.com/apache/dubbo-admin/pkg/console/model"
+)
 
-func MeshNotFound(meshName string) error {
-       return &MeshNotFoundError{meshName}
+func HandleServiceError(ctx *gin.Context, err error) {
+       var e bizerror.Error
+       if !errors.As(err, &e) {
+               e = bizerror.NewBizError(bizerror.UnknownError, err.Error())
+       }
+       ctx.JSON(http.StatusOK, model.NewBizErrorResp(e))
 }
 
-func IsMeshNotFound(err error) bool {
-       var meshNotFoundError *MeshNotFoundError
-       ok := errors.As(err, &meshNotFoundError)
-       return ok
+func HandleArgumentError(ctx *gin.Context, err error) {
+       e := bizerror.NewBizError(bizerror.InvalidArgument, err.Error())
+       ctx.JSON(http.StatusOK, model.NewBizErrorResp(e))
 }

Reply via email to