From 68bf5dc37bdf8a58e71b3303cf3f010eeec5f0af Mon Sep 17 00:00:00 2001 From: WinfredLIN Date: Wed, 11 Mar 2026 09:47:29 +0000 Subject: [PATCH 1/2] Add custom HTTP error handler to APIServer --- internal/apiserver/service/service.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/internal/apiserver/service/service.go b/internal/apiserver/service/service.go index 69591e55..b507b425 100644 --- a/internal/apiserver/service/service.go +++ b/internal/apiserver/service/service.go @@ -52,6 +52,23 @@ func NewAPIServer(logger utilLog.Logger, opts *conf.DMSOptions) (*APIServer, err e := echo.New() e.HideBanner = true e.HidePort = true + e.HTTPErrorHandler = func(err error, c echo.Context) { + if c.Response().Committed { + return + } + + message := err.Error() + if httpErr, ok := err.(*echo.HTTPError); ok { + if msg, ok := httpErr.Message.(string); ok && msg != "" { + message = msg + } + } + + _ = c.JSON(http.StatusBadRequest, bV1.GenericResp{ + Code: int(apiError.BadRequestErr), + Message: message, + }) + } return &APIServer{ logger: logger, opts: opts, From 1d9a28b71cfc160cd5bbc52975215f60c3dd9d06 Mon Sep 17 00:00:00 2001 From: WinfredLIN Date: Wed, 11 Mar 2026 09:47:41 +0000 Subject: [PATCH 2/2] Add localized error messages for SQL Workbench audit functionality in English and Chinese, enhancing user feedback for various error scenarios. --- internal/pkg/locale/active.en.toml | 9 +++++++++ internal/pkg/locale/active.zh.toml | 9 +++++++++ internal/pkg/locale/message_zh.go | 13 ++++++++++++ .../service/sql_workbench_service.go | 20 ++++++++++--------- 4 files changed, 42 insertions(+), 9 deletions(-) diff --git a/internal/pkg/locale/active.en.toml b/internal/pkg/locale/active.en.toml index 9f2809e7..7efe81a9 100644 --- a/internal/pkg/locale/active.en.toml +++ b/internal/pkg/locale/active.en.toml @@ -208,6 +208,15 @@ ProjectDesc = "Project description" ProjectName = "Project name" ProjectNotAvailable = "Unavailable" ProjectStatus = "Project status" +SqlWorkbenchAuditCallSQLEErr = "The audit service is busy or unavailable. Please try again later." +SqlWorkbenchAuditDBServiceMappingNotFoundErr = "Current data source was not found. Please confirm it exists, reselect it, and try again." +SqlWorkbenchAuditGetDBServiceErr = "Current data source configuration was not found. Please confirm the data source exists and try again." +SqlWorkbenchAuditGetDBServiceMappingErr = "We couldn't get the current data source information. Please reselect the data source and try again." +SqlWorkbenchAuditGetDMSUserErr = "Your login session may have expired. Please sign in again and retry." +SqlWorkbenchAuditMissingSQLOrDatasourceErr = "SQL or data source was not detected. Please select a data source and enter SQL, then try again." +SqlWorkbenchAuditNotEnabledErr = "SQL audit is not enabled for this data source. Please enable SQL audit before running." +SqlWorkbenchAuditParseReqErr = "We couldn't parse the request content. Please try again later." +SqlWorkbenchAuditReadReqBodyErr = "We couldn't get the request content, possibly due to network instability. Please try again later." StatDisable = "Disabled" StatOK = "Normal" StatUnknown = "Unknown" diff --git a/internal/pkg/locale/active.zh.toml b/internal/pkg/locale/active.zh.toml index d7571851..ae65b793 100644 --- a/internal/pkg/locale/active.zh.toml +++ b/internal/pkg/locale/active.zh.toml @@ -208,6 +208,15 @@ ProjectDesc = "项目描述" ProjectName = "项目名称" ProjectNotAvailable = "不可用" ProjectStatus = "项目状态" +SqlWorkbenchAuditCallSQLEErr = "审核服务当前繁忙或不可用,请稍后重试。" +SqlWorkbenchAuditDBServiceMappingNotFoundErr = "未找到当前数据源,请确认数据源存在并重新选择后重试。" +SqlWorkbenchAuditGetDBServiceErr = "未找到当前数据源配置,请确认数据源存在后重试。" +SqlWorkbenchAuditGetDBServiceMappingErr = "当前数据源信息获取失败,请重新选择数据源后重试。" +SqlWorkbenchAuditGetDMSUserErr = "当前登录状态可能已过期,请重新登录后再试。" +SqlWorkbenchAuditMissingSQLOrDatasourceErr = "未检测到 SQL 或数据源,请确认已选择数据源并输入 SQL 后重试。" +SqlWorkbenchAuditNotEnabledErr = "该数据源尚未开启 SQL 审核,请先在数据源配置中开启“SQL 审核”后再执行。" +SqlWorkbenchAuditParseReqErr = "请求内容解析失败,请稍后重试。" +SqlWorkbenchAuditReadReqBodyErr = "请求内容获取失败,可能网络不稳定,请稍后重试。" StatDisable = "被禁用" StatOK = "正常" StatUnknown = "未知" diff --git a/internal/pkg/locale/message_zh.go b/internal/pkg/locale/message_zh.go index 7244ab56..87c9c9a8 100644 --- a/internal/pkg/locale/message_zh.go +++ b/internal/pkg/locale/message_zh.go @@ -182,6 +182,19 @@ var ( CbOpResultRowCount = &i18n.Message{ID: "CbOpResultRowCount", Other: "结果集返回行数"} ) +// SQL Workbench +var ( + SqlWorkbenchAuditReadReqBodyErr = &i18n.Message{ID: "SqlWorkbenchAuditReadReqBodyErr", Other: "请求内容获取失败,可能网络不稳定,请稍后重试。"} + SqlWorkbenchAuditParseReqErr = &i18n.Message{ID: "SqlWorkbenchAuditParseReqErr", Other: "请求内容解析失败,请稍后重试。"} + SqlWorkbenchAuditMissingSQLOrDatasourceErr = &i18n.Message{ID: "SqlWorkbenchAuditMissingSQLOrDatasourceErr", Other: "未检测到 SQL 或数据源,请确认已选择数据源并输入 SQL 后重试。"} + SqlWorkbenchAuditGetDMSUserErr = &i18n.Message{ID: "SqlWorkbenchAuditGetDMSUserErr", Other: "当前登录状态可能已过期,请重新登录后再试。"} + SqlWorkbenchAuditGetDBServiceMappingErr = &i18n.Message{ID: "SqlWorkbenchAuditGetDBServiceMappingErr", Other: "当前数据源信息获取失败,请重新选择数据源后重试。"} + SqlWorkbenchAuditDBServiceMappingNotFoundErr = &i18n.Message{ID: "SqlWorkbenchAuditDBServiceMappingNotFoundErr", Other: "未找到当前数据源,请确认数据源存在并重新选择后重试。"} + SqlWorkbenchAuditGetDBServiceErr = &i18n.Message{ID: "SqlWorkbenchAuditGetDBServiceErr", Other: "未找到当前数据源配置,请确认数据源存在后重试。"} + SqlWorkbenchAuditNotEnabledErr = &i18n.Message{ID: "SqlWorkbenchAuditNotEnabledErr", Other: "该数据源尚未开启 SQL 审核,请先在数据源配置中开启“SQL 审核”后再执行。"} + SqlWorkbenchAuditCallSQLEErr = &i18n.Message{ID: "SqlWorkbenchAuditCallSQLEErr", Other: "审核服务当前繁忙或不可用,请稍后重试。"} +) + // DB Service Sync Task var ( DBServiceSyncVersion = &i18n.Message{ID: "DBServiceSyncVersion", Other: "版本(支持DMP5.23.04.0及以上版本)"} diff --git a/internal/sql_workbench/service/sql_workbench_service.go b/internal/sql_workbench/service/sql_workbench_service.go index ecda8d65..c261514a 100644 --- a/internal/sql_workbench/service/sql_workbench_service.go +++ b/internal/sql_workbench/service/sql_workbench_service.go @@ -6,6 +6,7 @@ import ( "context" "encoding/base64" "encoding/json" + "errors" "fmt" "io" "net/http" @@ -22,6 +23,7 @@ import ( "github.com/actiontech/dms/internal/dms/biz" pkgConst "github.com/actiontech/dms/internal/dms/pkg/constant" "github.com/actiontech/dms/internal/dms/storage" + "github.com/actiontech/dms/internal/pkg/locale" dbmodel "github.com/actiontech/dms/internal/dms/storage/model" "github.com/actiontech/dms/internal/sql_workbench/client" config "github.com/actiontech/dms/internal/sql_workbench/config" @@ -1020,7 +1022,7 @@ func (sqlWorkbenchService *SqlWorkbenchService) AuditMiddleware() echo.Middlewar bodyBytes, err := io.ReadAll(c.Request().Body) if err != nil { sqlWorkbenchService.log.Errorf("failed to read request body: %v", err) - return fmt.Errorf("failed to read request body: %w", err) + return errors.New(locale.Bundle.LocalizeMsgByCtx(c.Request().Context(), locale.SqlWorkbenchAuditReadReqBodyErr)) } // 恢复请求体,供后续处理使用 c.Request().Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) @@ -1029,51 +1031,51 @@ func (sqlWorkbenchService *SqlWorkbenchService) AuditMiddleware() echo.Middlewar sql, datasourceID, err := sqlWorkbenchService.parseStreamExecuteRequest(bodyBytes) if err != nil { sqlWorkbenchService.log.Errorf("failed to parse streamExecute request, skipping audit: %v", err) - return fmt.Errorf("failed to parse streamExecute request, skipping audit: %v", err) + return errors.New(locale.Bundle.LocalizeMsgByCtx(c.Request().Context(), locale.SqlWorkbenchAuditParseReqErr)) } if sql == "" || datasourceID == "" { sqlWorkbenchService.log.Debugf("SQL or datasource ID is empty, skipping audit") - return fmt.Errorf("SQL or datasource ID is empty, skipping audit") + return errors.New(locale.Bundle.LocalizeMsgByCtx(c.Request().Context(), locale.SqlWorkbenchAuditMissingSQLOrDatasourceErr)) } // 获取当前用户 ID dmsUserId, err := sqlWorkbenchService.getDMSUserIdFromRequest(c) if err != nil { sqlWorkbenchService.log.Errorf("failed to get DMS user ID: %v", err) - return fmt.Errorf("failed to get DMS user ID: %v", err) + return errors.New(locale.Bundle.LocalizeMsgByCtx(c.Request().Context(), locale.SqlWorkbenchAuditGetDMSUserErr)) } // 从缓存表获取 dms_db_service_id dmsDBServiceID, err := sqlWorkbenchService.getDMSDBServiceIDFromCache(c.Request().Context(), datasourceID, dmsUserId) if err != nil { sqlWorkbenchService.log.Errorf("failed to get dms_db_service_id from cache: %v", err) - return fmt.Errorf("failed to get dms_db_service_id from cache: %v", err) + return errors.New(locale.Bundle.LocalizeMsgByCtx(c.Request().Context(), locale.SqlWorkbenchAuditGetDBServiceMappingErr)) } if dmsDBServiceID == "" { sqlWorkbenchService.log.Debugf("dms_db_service_id not found in cache for datasource: %s", datasourceID) - return fmt.Errorf("dms_db_service_id not found in cache for datasource: %s", datasourceID) + return errors.New(locale.Bundle.LocalizeMsgByCtx(c.Request().Context(), locale.SqlWorkbenchAuditDBServiceMappingNotFoundErr)) } // 获取 DBService 信息 dbService, err := sqlWorkbenchService.dbServiceUsecase.GetDBService(c.Request().Context(), dmsDBServiceID) if err != nil { sqlWorkbenchService.log.Errorf("failed to get DBService: %v", err) - return fmt.Errorf("failed to get DBService: %v", err) + return errors.New(locale.Bundle.LocalizeMsgByCtx(c.Request().Context(), locale.SqlWorkbenchAuditGetDBServiceErr)) } // 检查是否启用 SQL 审核 if !sqlWorkbenchService.isEnableSQLAudit(dbService) { sqlWorkbenchService.log.Debugf("SQL audit is not enabled for DBService: %s", dmsDBServiceID) - return fmt.Errorf("SQL audit is not enabled for DBService: %s", dmsDBServiceID) + return errors.New(locale.Bundle.LocalizeMsgByCtx(c.Request().Context(), locale.SqlWorkbenchAuditNotEnabledErr)) } // 调用 SQLE 审核接口 auditResult, err := sqlWorkbenchService.callSQLEAudit(c.Request().Context(), sql, dbService) if err != nil { sqlWorkbenchService.log.Errorf("call SQLE audit failed: %v", err) - return fmt.Errorf("call SQLE audit failed: %v", err) + return errors.New(locale.Bundle.LocalizeMsgByCtx(c.Request().Context(), locale.SqlWorkbenchAuditCallSQLEErr)) } // 拦截响应并添加审核结果