From f486e2715555cfce153bba13bfbc28f45b075887 Mon Sep 17 00:00:00 2001 From: yangzhongjiao <451773851@qq.com> Date: Thu, 19 Aug 2021 17:33:32 +0800 Subject: [PATCH 1/5] add api white list , user/role operations are controlled by admin --- drivers/api/handler/v2/role.go | 39 ++++++++++++++++++++++++++++++---- drivers/api/handler/v2/user.go | 5 +++++ drivers/api/route.go | 36 +++++++++++++++++++++++++------ 3 files changed, 69 insertions(+), 11 deletions(-) diff --git a/drivers/api/handler/v2/role.go b/drivers/api/handler/v2/role.go index 9fa209df7..5ff20d58a 100644 --- a/drivers/api/handler/v2/role.go +++ b/drivers/api/handler/v2/role.go @@ -67,7 +67,10 @@ func CreateRoleV2(c echo.Context) error { if err := handler.BindAndValidate(logger, c, reqParam); err != nil { return err } - + tenant, _ := GetUserName(c) + if !roleAccess(tenant, reqParam.Tenant) { + return c.JSON(http.StatusForbidden, models.BuildBaseResp(fmt.Errorf("current user cannot create tenant [ %v ] role [ %v ]", reqParam.Tenant, reqParam.Name))) + } err := UpdateRoleInfo(logger, &common.Role{ Tenant: reqParam.Tenant, Name: reqParam.Name, @@ -94,7 +97,11 @@ func UpdateRoleV2(c echo.Context) error { return err } if reqParam.Name == common.DefaultRole { - return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(fmt.Errorf("admin role does not support modification"))) + return c.JSON(http.StatusForbidden, models.BuildBaseResp(fmt.Errorf("admin role does not support modification"))) + } + tenant, _ := GetUserName(c) + if !roleAccess(tenant, reqParam.Tenant) { + return c.JSON(http.StatusForbidden, models.BuildBaseResp(fmt.Errorf("current user cannot update tenant [ %v ] role [ %v ]", reqParam.Tenant, reqParam.Name))) } err := UpdateRoleInfo(logger, &common.Role{ Tenant: reqParam.Tenant, @@ -146,13 +153,28 @@ func DeleteRoleV2(c echo.Context) error { return err } // cannot delete default supper role - if reqParam.Tenant == common.DefaultAdminTenant && reqParam.Name == common.DefaultAdminUser { - return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(fmt.Errorf("cannot delete current role"))) + if reqParam.Tenant == common.DefaultAdminTenant && reqParam.Name == common.DefaultRole { + return c.JSON(http.StatusForbidden, models.BuildBaseResp(fmt.Errorf("cannot delete tenant [ %v ] role [ %v ]", reqParam.Tenant, reqParam.Name))) + } + tenant, _ := GetUserName(c) + if !roleAccess(tenant, reqParam.Tenant) { + return c.JSON(http.StatusForbidden, models.BuildBaseResp(fmt.Errorf("cannot delete tenant [ %v ] role [ %v ]", reqParam.Tenant, reqParam.Name))) } storeManager, err := common.NewStoreManager([]string{handler.ConsulAddr}, logger) if err != nil { return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(fmt.Errorf("get consul client failed: %v", err))) } + // make sure there is no user in the current role + users, err := storeManager.FindUserList(reqParam.Tenant) + if err != nil { + return c.JSON(http.StatusInternalServerError, + models.BuildBaseResp(fmt.Errorf("find tenant [ %v ] user list from consul failed: %v", reqParam.Tenant, err))) + } + for _, user := range users { + if user.Role == reqParam.Name { + return c.JSON(http.StatusOK, models.BuildBaseResp(fmt.Errorf("there are users in the current tenant [ %v ] role [ %v ]", reqParam.Tenant, reqParam.Name))) + } + } if err := storeManager.DeleteRole(reqParam.Tenant, reqParam.Name); nil != err { return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(fmt.Errorf("delete metadata of Role[RoleName=%v,RoleGroup=%v] from consul failed: %v", reqParam.Name, reqParam.Tenant, err))) @@ -162,3 +184,12 @@ func DeleteRoleV2(c echo.Context) error { BaseResp: models.BuildBaseResp(nil), }) } + +// The platform administrator can operate all roles +// the tenant administrator can only operate the roles under the current tenant +func roleAccess(currentTenant, operationTenant string) bool { + if currentTenant == common.DefaultAdminTenant || currentTenant == operationTenant { + return true + } + return false +} diff --git a/drivers/api/handler/v2/user.go b/drivers/api/handler/v2/user.go index 2df2cfc35..f7ea0a7cc 100644 --- a/drivers/api/handler/v2/user.go +++ b/drivers/api/handler/v2/user.go @@ -271,6 +271,11 @@ func DeleteUserV2(c echo.Context) error { if reqParam.Tenant == common.DefaultAdminTenant && reqParam.Username == common.DefaultAdminUser { return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(fmt.Errorf("cannot delete superuser"))) } + tenant, user := GetUserName(c) + // cannot delete self + if reqParam.Tenant == tenant && reqParam.Username == user { + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(fmt.Errorf("cannot delete self"))) + } if hasAccess, err := checkUserAccess(logger, c, fmt.Sprintf("%s:%s", reqParam.Tenant, reqParam.Username)); err != nil || !hasAccess { return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(fmt.Errorf("current user has no access to operate group %v ; err : %v", reqParam.Tenant, err))) diff --git a/drivers/api/route.go b/drivers/api/route.go index aa5ef16db..6387e279d 100644 --- a/drivers/api/route.go +++ b/drivers/api/route.go @@ -82,6 +82,7 @@ func SetupApiServer(logger hclog.Logger, driverConfig *mysql.DriverConfig) (err v2Router.Use(JWTTokenAdapter(), middleware.JWT([]byte(common.JWTSecret)), AuthFilter()) e.POST("/v2/login", v2.LoginV2) e.POST("/v2/login/captcha", v2.CaptchaV2) + e.GET("/v2/monitor/task", v2.GetTaskProgressV2) v2Router.POST("/log/level", v2.UpdateLogLevelV2, AdminUserAllowed()) v2Router.GET("/jobs", v2.JobListV2) v2Router.GET("/job/migration/detail", v2.GetMigrationJobDetailV2) @@ -98,19 +99,18 @@ func SetupApiServer(logger hclog.Logger, driverConfig *mysql.DriverConfig) (err v2Router.GET("/mysql/schemas", v2.ListMysqlSchemasV2) v2Router.GET("/mysql/columns", v2.ListMysqlColumnsV2) v2Router.GET("/mysql/instance_connection", v2.ConnectionV2) - v2Router.GET("/monitor/task", v2.GetTaskProgressV2) v2Router.GET("/job/gtid", v2.GetJobGtidV2) v2Router.POST("/job/reverse_start", v2.ReverseStartJobV2) v2Router.POST("/job/reverse", v2.ReverseJobV2) - v2Router.GET("/user/list", v2.UserListV2) - v2Router.POST("/user/create", v2.CreateUserV2) + v2Router.GET("/user/list", v2.UserListV2, AdminUserAllowed()) + v2Router.POST("/user/create", v2.CreateUserV2, AdminUserAllowed()) v2Router.POST("/user/update", v2.UpdateUserV2) v2Router.POST("/user/reset_password", v2.ResetPasswordV2) - v2Router.POST("/user/delete", v2.DeleteUserV2) - v2Router.GET("/tenant/list", v2.TenantListV2) + v2Router.POST("/user/delete", v2.DeleteUserV2, AdminUserAllowed()) + v2Router.GET("/tenant/list", v2.TenantListV2, AdminUserAllowed()) v2Router.GET("/user/current_user", v2.GetCurrentUserV2) v2Router.GET("/user/list_action", v2.ListActionV2) - v2Router.GET("/role/list", v2.RoleListV2) + v2Router.GET("/role/list", v2.RoleListV2, AdminUserAllowed()) v2Router.POST("/role/create", v2.CreateRoleV2, AdminUserAllowed()) v2Router.POST("/role/delete", v2.DeleteRoleV2, AdminUserAllowed()) v2Router.POST("/role/update", v2.UpdateRoleV2, AdminUserAllowed()) @@ -184,6 +184,9 @@ func JWTTokenAdapter() echo.MiddlewareFunc { func AuthFilter() echo.MiddlewareFunc { return func(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { + if inWhiteList(c.Request().URL.Path) { + return next(c) + } logger := handler.NewLogger().Named("AuthFilter") storeManager, err := common.NewStoreManager([]string{handler.ConsulAddr}, logger) if err != nil { @@ -223,7 +226,7 @@ func AuthFilter() echo.MiddlewareFunc { func AdminUserAllowed() echo.MiddlewareFunc { return func(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { - logger := handler.NewLogger().Named("ResumeJobV2") + logger := handler.NewLogger().Named("AdminUserAllowed") tenant, user := v2.GetUserName(c) storeManager, err := common.NewStoreManager([]string{handler.ConsulAddr}, logger) if err != nil { @@ -238,6 +241,25 @@ func AdminUserAllowed() echo.MiddlewareFunc { } } +var whiteList = []string{ + "/v2/nodes", + "/v2/validation/job", + "/v2/mysql/schemas", + "/v2/mysql/columns", + "/v2/mysql/instance_connection", + "/v2/job/gtid", + "/v2/user/list_action", + "/v2/user/current_user", +} + +func inWhiteList(uri string) bool { + for _, whiteUri := range whiteList { + if uri == whiteUri { + return true + } + } + return false +} func init() { middleware.ErrJWTMissing.Code = http.StatusUnauthorized middleware.ErrJWTMissing.Message = "permission denied,please login again!" From 3dbbf982af892394b925841a8d046f960a156c94 Mon Sep 17 00:00:00 2001 From: yangzhongjiao <451773851@qq.com> Date: Thu, 19 Aug 2021 20:31:44 +0800 Subject: [PATCH 2/5] modify the data format of operation authority and createOrUpdate interface split --- drivers/api/docs/docs.go | 192 +++++++++++++++++++++++---- drivers/api/docs/swagger.json | 192 +++++++++++++++++++++++---- drivers/api/docs/swagger.yaml | 127 +++++++++++++++--- drivers/api/handler/v2/job.go | 141 +++++++++++++------- drivers/api/handler/v2/log.go | 2 +- drivers/api/handler/v2/login.go | 4 +- drivers/api/handler/v2/monitor.go | 2 +- drivers/api/handler/v2/mysql.go | 6 +- drivers/api/handler/v2/role.go | 8 +- drivers/api/handler/v2/user.go | 12 +- drivers/api/handler/v2/validation.go | 2 +- drivers/api/models/role.go | 17 ++- drivers/api/models/user.go | 2 +- drivers/api/route.go | 110 ++++++++++----- drivers/mysql/common/common.go | 2 +- drivers/mysql/common/store.go | 6 + 16 files changed, 660 insertions(+), 165 deletions(-) diff --git a/drivers/api/docs/docs.go b/drivers/api/docs/docs.go index 147db740d..91f6696c8 100644 --- a/drivers/api/docs/docs.go +++ b/drivers/api/docs/docs.go @@ -89,21 +89,21 @@ var doc = `{ } } }, - "/v2/job/migration": { + "/v2/job/migration/create": { "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "create or update migration job.", + "description": "create migration job.", "consumes": [ "application/json" ], "tags": [ "job" ], - "operationId": "CreateOrUpdateMigrationJobV2", + "operationId": "CreateMigrationJobV2", "parameters": [ { "description": "migration job config", @@ -156,6 +156,42 @@ var doc = `{ } } }, + "/v2/job/migration/update": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "update migration job.", + "consumes": [ + "application/json" + ], + "tags": [ + "job" + ], + "operationId": "UpdateMigrationJobV2", + "parameters": [ + { + "description": "migration job config", + "name": "migration_job_config", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.CreateOrUpdateMysqlToMysqlJobParamV2" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.CreateOrUpdateMysqlToMysqlJobRespV2" + } + } + } + } + }, "/v2/job/pause": { "post": { "security": [ @@ -331,6 +367,42 @@ var doc = `{ } } }, + "/v2/job/subscription/create": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "create subscription job.", + "consumes": [ + "application/json" + ], + "tags": [ + "job" + ], + "operationId": "CreateSubscriptionJobV2", + "parameters": [ + { + "description": "subscription job config", + "name": "subscription_job_config", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.CreateOrUpdateMysqlToKafkaJobParamV2" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.CreateOrUpdateMysqlToKafkaJobRespV2" + } + } + } + } + }, "/v2/job/subscription/detail": { "get": { "security": [ @@ -362,21 +434,21 @@ var doc = `{ } } }, - "/v2/job/sync": { + "/v2/job/sync/create": { "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "create or update sync job.", + "description": "create sync job.", "consumes": [ "application/json" ], "tags": [ "job" ], - "operationId": "CreateOrUpdateSyncJobV2", + "operationId": "CreateSyncJobV2", "parameters": [ { "description": "sync job config", @@ -429,6 +501,42 @@ var doc = `{ } } }, + "/v2/job/sync/update": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "update sync job.", + "consumes": [ + "application/json" + ], + "tags": [ + "job" + ], + "operationId": "UpdateSyncJobV2", + "parameters": [ + { + "description": "sync job config", + "name": "sync_job_config", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.CreateOrUpdateMysqlToMysqlJobParamV2" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.CreateOrUpdateMysqlToMysqlJobRespV2" + } + } + } + } + }, "/v2/jobs": { "get": { "security": [ @@ -1386,17 +1494,6 @@ var doc = `{ } } }, - "models.ActionItem": { - "type": "object", - "properties": { - "action": { - "type": "string" - }, - "uri": { - "type": "string" - } - } - }, "models.BasicTaskProfile": { "type": "object", "properties": { @@ -1461,6 +1558,23 @@ var doc = `{ } } }, + "models.ButtonItem": { + "type": "object", + "properties": { + "action": { + "type": "string" + }, + "text_cn": { + "type": "string" + }, + "text_en": { + "type": "string" + }, + "uri": { + "type": "string" + } + } + }, "models.CaptchaRespV2": { "type": "object", "properties": { @@ -1969,12 +2083,9 @@ var doc = `{ "type": "object", "properties": { "authority": { - "type": "object", - "additionalProperties": { - "type": "array", - "items": { - "$ref": "#/definitions/models.ActionItem" - } + "type": "array", + "items": { + "$ref": "#/definitions/models.MenuItem" } }, "message": { @@ -2010,6 +2121,41 @@ var doc = `{ } } }, + "models.MenuItem": { + "type": "object", + "properties": { + "admin_only": { + "type": "boolean" + }, + "id": { + "type": "integer" + }, + "menu_level": { + "type": "integer" + }, + "menu_url": { + "type": "string" + }, + "name": { + "type": "string" + }, + "operations": { + "type": "array", + "items": { + "$ref": "#/definitions/models.ButtonItem" + } + }, + "parent_id": { + "type": "integer" + }, + "text_cn": { + "type": "string" + }, + "text_en": { + "type": "string" + } + } + }, "models.MysqlConnectionConfig": { "type": "object", "required": [ diff --git a/drivers/api/docs/swagger.json b/drivers/api/docs/swagger.json index e854e37cb..8f2769c18 100644 --- a/drivers/api/docs/swagger.json +++ b/drivers/api/docs/swagger.json @@ -73,21 +73,21 @@ } } }, - "/v2/job/migration": { + "/v2/job/migration/create": { "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "create or update migration job.", + "description": "create migration job.", "consumes": [ "application/json" ], "tags": [ "job" ], - "operationId": "CreateOrUpdateMigrationJobV2", + "operationId": "CreateMigrationJobV2", "parameters": [ { "description": "migration job config", @@ -140,6 +140,42 @@ } } }, + "/v2/job/migration/update": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "update migration job.", + "consumes": [ + "application/json" + ], + "tags": [ + "job" + ], + "operationId": "UpdateMigrationJobV2", + "parameters": [ + { + "description": "migration job config", + "name": "migration_job_config", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.CreateOrUpdateMysqlToMysqlJobParamV2" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.CreateOrUpdateMysqlToMysqlJobRespV2" + } + } + } + } + }, "/v2/job/pause": { "post": { "security": [ @@ -315,6 +351,42 @@ } } }, + "/v2/job/subscription/create": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "create subscription job.", + "consumes": [ + "application/json" + ], + "tags": [ + "job" + ], + "operationId": "CreateSubscriptionJobV2", + "parameters": [ + { + "description": "subscription job config", + "name": "subscription_job_config", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.CreateOrUpdateMysqlToKafkaJobParamV2" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.CreateOrUpdateMysqlToKafkaJobRespV2" + } + } + } + } + }, "/v2/job/subscription/detail": { "get": { "security": [ @@ -346,21 +418,21 @@ } } }, - "/v2/job/sync": { + "/v2/job/sync/create": { "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "create or update sync job.", + "description": "create sync job.", "consumes": [ "application/json" ], "tags": [ "job" ], - "operationId": "CreateOrUpdateSyncJobV2", + "operationId": "CreateSyncJobV2", "parameters": [ { "description": "sync job config", @@ -413,6 +485,42 @@ } } }, + "/v2/job/sync/update": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "update sync job.", + "consumes": [ + "application/json" + ], + "tags": [ + "job" + ], + "operationId": "UpdateSyncJobV2", + "parameters": [ + { + "description": "sync job config", + "name": "sync_job_config", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.CreateOrUpdateMysqlToMysqlJobParamV2" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.CreateOrUpdateMysqlToMysqlJobRespV2" + } + } + } + } + }, "/v2/jobs": { "get": { "security": [ @@ -1370,17 +1478,6 @@ } } }, - "models.ActionItem": { - "type": "object", - "properties": { - "action": { - "type": "string" - }, - "uri": { - "type": "string" - } - } - }, "models.BasicTaskProfile": { "type": "object", "properties": { @@ -1445,6 +1542,23 @@ } } }, + "models.ButtonItem": { + "type": "object", + "properties": { + "action": { + "type": "string" + }, + "text_cn": { + "type": "string" + }, + "text_en": { + "type": "string" + }, + "uri": { + "type": "string" + } + } + }, "models.CaptchaRespV2": { "type": "object", "properties": { @@ -1953,12 +2067,9 @@ "type": "object", "properties": { "authority": { - "type": "object", - "additionalProperties": { - "type": "array", - "items": { - "$ref": "#/definitions/models.ActionItem" - } + "type": "array", + "items": { + "$ref": "#/definitions/models.MenuItem" } }, "message": { @@ -1994,6 +2105,41 @@ } } }, + "models.MenuItem": { + "type": "object", + "properties": { + "admin_only": { + "type": "boolean" + }, + "id": { + "type": "integer" + }, + "menu_level": { + "type": "integer" + }, + "menu_url": { + "type": "string" + }, + "name": { + "type": "string" + }, + "operations": { + "type": "array", + "items": { + "$ref": "#/definitions/models.ButtonItem" + } + }, + "parent_id": { + "type": "integer" + }, + "text_cn": { + "type": "string" + }, + "text_en": { + "type": "string" + } + } + }, "models.MysqlConnectionConfig": { "type": "object", "required": [ diff --git a/drivers/api/docs/swagger.yaml b/drivers/api/docs/swagger.yaml index 6e5981ab3..d5d0b7ccb 100644 --- a/drivers/api/docs/swagger.yaml +++ b/drivers/api/docs/swagger.yaml @@ -66,13 +66,6 @@ definitions: username: type: string type: object - models.ActionItem: - properties: - action: - type: string - uri: - type: string - type: object models.BasicTaskProfile: properties: configuration: @@ -115,6 +108,17 @@ definitions: send_by_timeout: type: integer type: object + models.ButtonItem: + properties: + action: + type: string + text_cn: + type: string + text_en: + type: string + uri: + type: string + type: object models.CaptchaRespV2: properties: data_scheme: @@ -452,11 +456,9 @@ definitions: models.ListActionRespV2: properties: authority: - additionalProperties: - items: - $ref: '#/definitions/models.ActionItem' - type: array - type: object + items: + $ref: '#/definitions/models.MenuItem' + type: array message: type: string type: object @@ -478,6 +480,29 @@ definitions: $ref: '#/definitions/models.SchemaItem' type: array type: object + models.MenuItem: + properties: + admin_only: + type: boolean + id: + type: integer + menu_level: + type: integer + menu_url: + type: string + name: + type: string + operations: + items: + $ref: '#/definitions/models.ButtonItem' + type: array + parent_id: + type: integer + text_cn: + type: string + text_en: + type: string + type: object models.MysqlConnectionConfig: properties: mysql_host: @@ -988,12 +1013,12 @@ paths: - ApiKeyAuth: [] tags: - job - /v2/job/migration: + /v2/job/migration/create: post: consumes: - application/json - description: create or update migration job. - operationId: CreateOrUpdateMigrationJobV2 + description: create migration job. + operationId: CreateMigrationJobV2 parameters: - description: migration job config in: body @@ -1029,6 +1054,28 @@ paths: - ApiKeyAuth: [] tags: - job + /v2/job/migration/update: + post: + consumes: + - application/json + description: update migration job. + operationId: UpdateMigrationJobV2 + parameters: + - description: migration job config + in: body + name: migration_job_config + required: true + schema: + $ref: '#/definitions/models.CreateOrUpdateMysqlToMysqlJobParamV2' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.CreateOrUpdateMysqlToMysqlJobRespV2' + security: + - ApiKeyAuth: [] + tags: + - job /v2/job/pause: post: consumes: @@ -1137,6 +1184,28 @@ paths: - ApiKeyAuth: [] tags: - job + /v2/job/subscription/create: + post: + consumes: + - application/json + description: create subscription job. + operationId: CreateSubscriptionJobV2 + parameters: + - description: subscription job config + in: body + name: subscription_job_config + required: true + schema: + $ref: '#/definitions/models.CreateOrUpdateMysqlToKafkaJobParamV2' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.CreateOrUpdateMysqlToKafkaJobRespV2' + security: + - ApiKeyAuth: [] + tags: + - job /v2/job/subscription/detail: get: description: get subscription job detail. @@ -1156,12 +1225,12 @@ paths: - ApiKeyAuth: [] tags: - job - /v2/job/sync: + /v2/job/sync/create: post: consumes: - application/json - description: create or update sync job. - operationId: CreateOrUpdateSyncJobV2 + description: create sync job. + operationId: CreateSyncJobV2 parameters: - description: sync job config in: body @@ -1197,6 +1266,28 @@ paths: - ApiKeyAuth: [] tags: - job + /v2/job/sync/update: + post: + consumes: + - application/json + description: update sync job. + operationId: UpdateSyncJobV2 + parameters: + - description: sync job config + in: body + name: sync_job_config + required: true + schema: + $ref: '#/definitions/models.CreateOrUpdateMysqlToMysqlJobParamV2' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.CreateOrUpdateMysqlToMysqlJobRespV2' + security: + - ApiKeyAuth: [] + tags: + - job /v2/jobs: get: description: get job list. diff --git a/drivers/api/handler/v2/job.go b/drivers/api/handler/v2/job.go index c324f5ac2..943b6763d 100644 --- a/drivers/api/handler/v2/job.go +++ b/drivers/api/handler/v2/job.go @@ -42,7 +42,7 @@ func JobListV2(c echo.Context) error { logger := handler.NewLogger().Named("JobListV2") reqParam := new(models.JobListReqV2) if err := handler.BindAndValidate(logger, c, reqParam); err != nil { - return err + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } user, err := getCurrentUser(c) @@ -66,7 +66,7 @@ func JobListV2(c echo.Context) error { logger.Info("invoke nomad find job list finished") jobs := make([]common.JobListItemV2, 0) for _, consulJob := range jobList { - jobType := getJobTypeFromJobId(consulJob.JobId) + jobType := GetJobTypeFromJobId(consulJob.JobId) if "" != reqParam.FilterJobType && reqParam.FilterJobType != string(jobType) { continue } @@ -139,7 +139,7 @@ func addJobTypeToJobId(raw string, jobType DtleJobType) string { return fmt.Sprintf(`%v-%v`, raw, jobType) } -func getJobTypeFromJobId(jobId string) DtleJobType { +func GetJobTypeFromJobId(jobId string) DtleJobType { segs := strings.Split(jobId, "-") if len(segs) < 2 { return DtleJobTypeUnknown @@ -154,25 +154,14 @@ func getJobTypeFromJobId(jobId string) DtleJobType { } } -// @Id CreateOrUpdateMigrationJobV2 -// @Description create or update migration job. -// @Tags job -// @Accept application/json -// @Security ApiKeyAuth -// @Param migration_job_config body models.CreateOrUpdateMysqlToMysqlJobParamV2 true "migration job config" -// @Success 200 {object} models.CreateOrUpdateMysqlToMysqlJobRespV2 -// @Router /v2/job/migration [post] -func CreateOrUpdateMigrationJobV2(c echo.Context) error { - logger := handler.NewLogger().Named("CreateOrUpdateMigrationJobV2") +func CreateOrUpdateMigrationJobV2(c echo.Context, create bool) error { + logger := handler.NewLogger().Named("CreateMigrationJobV2") reqParam := new(models.CreateOrUpdateMysqlToMysqlJobParamV2) if err := handler.BindAndValidate(logger, c, reqParam); err != nil { - return err - } - if err := c.Validate(reqParam); nil != err { - return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(fmt.Errorf("invalid params:\n%v", err))) + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } - if err := checkUpdateJobInfo(c, reqParam.JobId); err != nil { + if err := checkUpdateJobInfo(c, reqParam.JobId, create); err != nil { return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } @@ -187,6 +176,30 @@ func CreateOrUpdateMigrationJobV2(c echo.Context) error { return c.JSON(http.StatusOK, resp) } +// @Id CreateMigrationJobV2 +// @Description create migration job. +// @Tags job +// @Accept application/json +// @Security ApiKeyAuth +// @Param migration_job_config body models.CreateOrUpdateMysqlToMysqlJobParamV2 true "migration job config" +// @Success 200 {object} models.CreateOrUpdateMysqlToMysqlJobRespV2 +// @Router /v2/job/migration/create [post] +func CreateMigrationJobV2(c echo.Context) error { + return CreateOrUpdateMigrationJobV2(c, true) +} + +// @Id UpdateMigrationJobV2 +// @Description update migration job. +// @Tags job +// @Accept application/json +// @Security ApiKeyAuth +// @Param migration_job_config body models.CreateOrUpdateMysqlToMysqlJobParamV2 true "migration job config" +// @Success 200 {object} models.CreateOrUpdateMysqlToMysqlJobRespV2 +// @Router /v2/job/migration/update [post] +func UpdateMigrationJobV2(c echo.Context) error { + return CreateOrUpdateMigrationJobV2(c, false) +} + func createOrUpdateMysqlToMysqlJob(logger hclog.Logger, jobParam *models.CreateOrUpdateMysqlToMysqlJobParamV2, user *common.User, jobType DtleJobType) (*models.CreateOrUpdateMysqlToMysqlJobRespV2, error) { @@ -491,7 +504,7 @@ func GetMysqlToMysqlJobDetail(c echo.Context, logger hclog.Logger, jobType DtleJ reqParam := new(models.MysqlToMysqlJobDetailReqV2) if err := handler.BindAndValidate(logger, c, reqParam); err != nil { - return err + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } err := checkJobAccess(c, reqParam.JobId) if err != nil { @@ -664,8 +677,8 @@ func getJobDetailFromNomad(logger hclog.Logger, jobId string, jobType DtleJobTyp } logger.Info("invoke nomad api finished") - if jobType != getJobTypeFromJobId(g.PtrToString(nomadJob.ID, "")) { - return false, nomadApi.Job{}, nil, fmt.Errorf("this API is for %v job. but got job type=%v by the provided job id", jobType, getJobTypeFromJobId(g.PtrToString(nomadJob.ID, ""))) + if jobType != GetJobTypeFromJobId(g.PtrToString(nomadJob.ID, "")) { + return false, nomadApi.Job{}, nil, fmt.Errorf("this API is for %v job. but got job type=%v by the provided job id", jobType, GetJobTypeFromJobId(g.PtrToString(nomadJob.ID, ""))) } url = handler.BuildUrl(fmt.Sprintf("/v1/job/%v/allocations", *nomadJob.ID)) logger.Info("invoke nomad api begin", "url", url) @@ -834,21 +847,13 @@ func buildMysqlToMysqlJobDetailResp(nomadJob nomadApi.Job, nomadAllocations []no return destTaskDetail, srcTaskDetail, nil } -// @Id CreateOrUpdateSyncJobV2 -// @Description create or update sync job. -// @Tags job -// @Accept application/json -// @Security ApiKeyAuth -// @Param sync_job_config body models.CreateOrUpdateMysqlToMysqlJobParamV2 true "sync job config" -// @Success 200 {object} models.CreateOrUpdateMysqlToMysqlJobRespV2 -// @Router /v2/job/sync [post] -func CreateOrUpdateSyncJobV2(c echo.Context) error { +func CreateOrUpdateSyncJobV2(c echo.Context, create bool) error { logger := handler.NewLogger().Named("CreateOrUpdateSyncJobV2") jobParam := new(models.CreateOrUpdateMysqlToMysqlJobParamV2) if err := handler.BindAndValidate(logger, c, jobParam); err != nil { - return err + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } - if err := checkUpdateJobInfo(c, jobParam.JobId); err != nil { + if err := checkUpdateJobInfo(c, jobParam.JobId, create); err != nil { return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } user, err := getCurrentUser(c) @@ -862,6 +867,30 @@ func CreateOrUpdateSyncJobV2(c echo.Context) error { return c.JSON(http.StatusOK, resp) } +// @Id CreateSyncJobV2 +// @Description create sync job. +// @Tags job +// @Accept application/json +// @Security ApiKeyAuth +// @Param sync_job_config body models.CreateOrUpdateMysqlToMysqlJobParamV2 true "sync job config" +// @Success 200 {object} models.CreateOrUpdateMysqlToMysqlJobRespV2 +// @Router /v2/job/sync/create [post] +func CreateSyncJobV2(c echo.Context) error { + return CreateOrUpdateSyncJobV2(c, true) +} + +// @Id UpdateSyncJobV2 +// @Description update sync job. +// @Tags job +// @Accept application/json +// @Security ApiKeyAuth +// @Param sync_job_config body models.CreateOrUpdateMysqlToMysqlJobParamV2 true "sync job config" +// @Success 200 {object} models.CreateOrUpdateMysqlToMysqlJobRespV2 +// @Router /v2/job/sync/update [post] +func UpdateSyncJobV2(c echo.Context) error { + return CreateOrUpdateSyncJobV2(c, false) +} + // @Id GetSyncJobDetailV2 // @Description get sync job detail. // @Tags job @@ -874,6 +903,19 @@ func GetSyncJobDetailV2(c echo.Context) error { return GetMysqlToMysqlJobDetail(c, logger, DtleJobTypeSync) } +// @Id CreateSubscriptionJobV2 +// @Description create subscription job. +// @Tags job +// @Accept application/json +// @Security ApiKeyAuth +// @Param subscription_job_config body models.CreateOrUpdateMysqlToKafkaJobParamV2 true "subscription job config" +// @Success 200 {object} models.CreateOrUpdateMysqlToKafkaJobRespV2 +// @Router /v2/job/subscription/create [post] +func CreateSubscriptionJobV2(c echo.Context) error { + logger := handler.NewLogger().Named("CreateSubscriptionJobV2") + return createOrUpdateMysqlToKafkaJob(c, logger, DtleJobTypeSubscription, true) +} + // @Id CreateOrUpdateSubscriptionJobV2 // @Description create or update subscription job. // @Tags job @@ -882,19 +924,19 @@ func GetSyncJobDetailV2(c echo.Context) error { // @Param subscription_job_config body models.CreateOrUpdateMysqlToKafkaJobParamV2 true "subscription job config" // @Success 200 {object} models.CreateOrUpdateMysqlToKafkaJobRespV2 // @Router /v2/job/subscription [post] -func CreateOrUpdateSubscriptionJobV2(c echo.Context) error { - logger := handler.NewLogger().Named("CreateOrUpdateSubscriptionJobV2") - return createOrUpdateMysqlToKafkaJob(c, logger, DtleJobTypeSubscription) +func UpdateSubscriptionJobV2(c echo.Context) error { + logger := handler.NewLogger().Named("UpdateSubscriptionJobV2") + return createOrUpdateMysqlToKafkaJob(c, logger, DtleJobTypeSubscription, false) } -func createOrUpdateMysqlToKafkaJob(c echo.Context, logger hclog.Logger, jobType DtleJobType) error { +func createOrUpdateMysqlToKafkaJob(c echo.Context, logger hclog.Logger, jobType DtleJobType, create bool) error { jobParam := new(models.CreateOrUpdateMysqlToKafkaJobParamV2) if err := handler.BindAndValidate(logger, c, jobParam); err != nil { - return err + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } - if err := checkUpdateJobInfo(c, jobParam.JobId); err != nil { + if err := checkUpdateJobInfo(c, jobParam.JobId, create); err != nil { return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } @@ -1015,7 +1057,7 @@ func GetSubscriptionJobDetailV2(c echo.Context) error { logger := handler.NewLogger().Named("GetSubscriptionJobDetailV2") reqParam := new(models.MysqlToKafkaJobDetailReqV2) if err := handler.BindAndValidate(logger, c, reqParam); err != nil { - return err + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } err := checkJobAccess(c, reqParam.JobId) @@ -1110,7 +1152,7 @@ func PauseJobV2(c echo.Context) error { logger := handler.NewLogger().Named("PauseJobV2") reqParam := new(models.PauseJobReqV2) if err := handler.BindAndValidate(logger, c, reqParam); err != nil { - return err + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } err := checkJobAccess(c, reqParam.JobId) @@ -1190,7 +1232,7 @@ func ResumeJobV2(c echo.Context) error { logger := handler.NewLogger().Named("ResumeJobV2") reqParam := new(models.ResumeJobReqV2) if err := handler.BindAndValidate(logger, c, reqParam); err != nil { - return err + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } err := checkJobAccess(c, reqParam.JobId) @@ -1286,7 +1328,7 @@ func DeleteJobV2(c echo.Context) error { logger := handler.NewLogger().Named("DeleteJobV2") reqParam := new(models.DeleteJobReqV2) if err := handler.BindAndValidate(logger, c, reqParam); err != nil { - return err + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } err := checkJobAccess(c, reqParam.JobId) if err != nil { @@ -1328,7 +1370,7 @@ func GetJobGtidV2(c echo.Context) error { logger := handler.NewLogger().Named("GetJobGtidV2") reqParam := new(models.GetJobGtidReqV2) if err := handler.BindAndValidate(logger, c, reqParam); err != nil { - return err + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } err := checkJobAccess(c, reqParam.JobId) @@ -1364,7 +1406,7 @@ func ReverseStartJobV2(c echo.Context) error { logger := handler.NewLogger().Named("ReverseStartJobV2") reqParam := new(models.ReverseStartReqV2) if err := handler.BindAndValidate(logger, c, reqParam); err != nil { - return err + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } err := checkJobAccess(c, reqParam.JobId) if err != nil { @@ -1372,7 +1414,7 @@ func ReverseStartJobV2(c echo.Context) error { } // get wait on job from current job detail info - _, nomadJob, allocations, err := getJobDetailFromNomad(logger, reqParam.JobId, getJobTypeFromJobId(reqParam.JobId)) + _, nomadJob, allocations, err := getJobDetailFromNomad(logger, reqParam.JobId, GetJobTypeFromJobId(reqParam.JobId)) if nil != err { return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } @@ -1442,7 +1484,7 @@ func ReverseJobV2(c echo.Context) error { reqParam := new(models.ReverseJobReq) if err := handler.BindAndValidate(logger, c, reqParam); err != nil { - return err + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } err := checkJobAccess(c, reqParam.JobId) if err != nil { @@ -1459,7 +1501,7 @@ func ReverseJobV2(c echo.Context) error { } // job name - jobType := getJobTypeFromJobId(consulJobItem.JobId) + jobType := GetJobTypeFromJobId(consulJobItem.JobId) switch jobType { case DtleJobTypeMigration, DtleJobTypeSync: originalJob, err := getMysqlToMysqlJobDetail(logger, consulJobItem.JobId, jobType) @@ -1571,13 +1613,16 @@ func checkJobAccess(c echo.Context, jobId string) error { return nil } -func checkUpdateJobInfo(c echo.Context, jobId string) error { +func checkUpdateJobInfo(c echo.Context, jobId string, create bool) error { logger := handler.NewLogger().Named("checkUpdateJobInfo") logger.Info("start checkUpdateJobInfo") storeManager, err := common.NewStoreManager([]string{handler.ConsulAddr}, logger) if err != nil { return fmt.Errorf("consul_addr=%v; connect to consul failed: %v", handler.ConsulAddr, err) } + if storeManager.CheckJobExists(jobId) != create { + return fmt.Errorf("please confirm whether the job [ %v ] already exists", jobId) + } jobInfo, err := storeManager.GetJobInfo(jobId) if nil != err { return fmt.Errorf("job_id=%v; get job status failed: %v", jobId, err) diff --git a/drivers/api/handler/v2/log.go b/drivers/api/handler/v2/log.go index 4aa2a9305..7aa797e50 100644 --- a/drivers/api/handler/v2/log.go +++ b/drivers/api/handler/v2/log.go @@ -32,7 +32,7 @@ func UpdateLogLevelV2(c echo.Context) error { reqParam := new(models.UpdataLogLevelReqV2) if err := handler.BindAndValidate(logger, c, reqParam); err != nil { - return err + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } // verify diff --git a/drivers/api/handler/v2/login.go b/drivers/api/handler/v2/login.go index 022e41356..353c6123c 100644 --- a/drivers/api/handler/v2/login.go +++ b/drivers/api/handler/v2/login.go @@ -31,7 +31,7 @@ func LoginV2(c echo.Context) error { once.Do(createPlatformUser) reqParam := new(models.UserLoginReqV2) if err := handler.BindAndValidate(logger, c, reqParam); err != nil { - return err + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } if !store.Verify(reqParam.CaptchaId, reqParam.Captcha, true) { return c.JSON(http.StatusBadRequest, models.BuildBaseResp(fmt.Errorf("verfied failed"))) @@ -82,7 +82,7 @@ func CaptchaV2(c echo.Context) error { logger := handler.NewLogger().Named("CaptchaV2") reqParam := new(models.VerifyCodeReqV2) if err := handler.BindAndValidate(logger, c, reqParam); err != nil { - return err + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } //create base64 encoding captcha diff --git a/drivers/api/handler/v2/monitor.go b/drivers/api/handler/v2/monitor.go index 70f90052c..b6539c154 100644 --- a/drivers/api/handler/v2/monitor.go +++ b/drivers/api/handler/v2/monitor.go @@ -27,7 +27,7 @@ func GetTaskProgressV2(c echo.Context) error { logger := handler.NewLogger().Named("GetTaskProgressV2") reqParam := new(models.GetTaskProgressReqV2) if err := handler.BindAndValidate(logger, c, reqParam); err != nil { - return err + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } targetNomadAddr := reqParam.NomadHttpAddress diff --git a/drivers/api/handler/v2/mysql.go b/drivers/api/handler/v2/mysql.go index c876d56be..463eaa923 100644 --- a/drivers/api/handler/v2/mysql.go +++ b/drivers/api/handler/v2/mysql.go @@ -31,7 +31,7 @@ func ListMysqlSchemasV2(c echo.Context) error { logger := handler.NewLogger().Named("ListMysqlSchemasV2") reqParam := new(models.ListDatabaseSchemasReqV2) if err := handler.BindAndValidate(logger, c, reqParam); err != nil { - return err + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } uri, err := buildMysqlUri(reqParam.MysqlHost, reqParam.MysqlUser, reqParam.MysqlPassword, @@ -102,7 +102,7 @@ func ListMysqlColumnsV2(c echo.Context) error { logger := handler.NewLogger().Named("ListMysqlColumnsV2") reqParam := new(models.ListColumnsReqV2) if err := handler.BindAndValidate(logger, c, reqParam); err != nil { - return err + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } uri, err := buildMysqlUri(reqParam.MysqlHost, reqParam.MysqlUser, reqParam.MysqlPassword, reqParam.MysqlCharacterSet, int(reqParam.MysqlPort), reqParam.IsMysqlPasswordEncrypted) @@ -168,7 +168,7 @@ func ConnectionV2(c echo.Context) error { logger := handler.NewLogger().Named("ConnectionV2") reqParam := new(models.ConnectionReqV2) if err := handler.BindAndValidate(logger, c, reqParam); err != nil { - return err + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } uri, err := buildMysqlUri(reqParam.MysqlHost, reqParam.MysqlUser, reqParam.MysqlPassword, diff --git a/drivers/api/handler/v2/role.go b/drivers/api/handler/v2/role.go index 5ff20d58a..5ac0a57ee 100644 --- a/drivers/api/handler/v2/role.go +++ b/drivers/api/handler/v2/role.go @@ -23,7 +23,7 @@ func RoleListV2(c echo.Context) error { logger := handler.NewLogger().Named("RoleListV2") reqParam := new(models.RoleListReq) if err := handler.BindAndValidate(logger, c, reqParam); err != nil { - return err + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } storeManager, err := common.NewStoreManager([]string{handler.ConsulAddr}, logger) @@ -65,7 +65,7 @@ func CreateRoleV2(c echo.Context) error { logger := handler.NewLogger().Named("CreateRoleV2") reqParam := new(models.CreateRoleReqV2) if err := handler.BindAndValidate(logger, c, reqParam); err != nil { - return err + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } tenant, _ := GetUserName(c) if !roleAccess(tenant, reqParam.Tenant) { @@ -94,7 +94,7 @@ func UpdateRoleV2(c echo.Context) error { logger := handler.NewLogger().Named("UpdateRoleV2") reqParam := new(models.UpdateRoleReqV2) if err := handler.BindAndValidate(logger, c, reqParam); err != nil { - return err + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } if reqParam.Name == common.DefaultRole { return c.JSON(http.StatusForbidden, models.BuildBaseResp(fmt.Errorf("admin role does not support modification"))) @@ -150,7 +150,7 @@ func DeleteRoleV2(c echo.Context) error { logger := handler.NewLogger().Named("DeleteRoleV2") reqParam := new(models.DeleteRoleReqV2) if err := handler.BindAndValidate(logger, c, reqParam); err != nil { - return err + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } // cannot delete default supper role if reqParam.Tenant == common.DefaultAdminTenant && reqParam.Name == common.DefaultRole { diff --git a/drivers/api/handler/v2/user.go b/drivers/api/handler/v2/user.go index f7ea0a7cc..fbbcdffca 100644 --- a/drivers/api/handler/v2/user.go +++ b/drivers/api/handler/v2/user.go @@ -29,7 +29,7 @@ func UserListV2(c echo.Context) error { logger := handler.NewLogger().Named("UserListV2") reqParam := new(models.UserListReq) if err := handler.BindAndValidate(logger, c, reqParam); err != nil { - return err + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } storeManager, err := common.NewStoreManager([]string{handler.ConsulAddr}, logger) @@ -106,7 +106,7 @@ func CreateUserV2(c echo.Context) error { logger := handler.NewLogger().Named("CreateUserV2") reqParam := new(models.CreateUserReqV2) if err := handler.BindAndValidate(logger, c, reqParam); err != nil { - return err + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } if hasAccess, err := checkUserAccess(logger, c, fmt.Sprintf("%s:%s", reqParam.Tenant, reqParam.Username)); err != nil || !hasAccess { @@ -170,7 +170,7 @@ func UpdateUserV2(c echo.Context) error { logger := handler.NewLogger().Named("UpdateUserV2") reqParam := new(models.UpdateUserReqV2) if err := handler.BindAndValidate(logger, c, reqParam); err != nil { - return err + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } storeManager, err := common.NewStoreManager([]string{handler.ConsulAddr}, logger) @@ -217,7 +217,7 @@ func ResetPasswordV2(c echo.Context) error { logger := handler.NewLogger().Named("ResetPasswordV2") reqParam := new(models.ResetPasswordReqV2) if err := handler.BindAndValidate(logger, c, reqParam); err != nil { - return err + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } if hasAccess, err := checkUserAccess(logger, c, fmt.Sprintf("%s:%s", reqParam.Tenant, reqParam.Username)); err != nil || !hasAccess { @@ -265,7 +265,7 @@ func DeleteUserV2(c echo.Context) error { logger := handler.NewLogger().Named("DeleteUserV2") reqParam := new(models.DeleteUserReqV2) if err := handler.BindAndValidate(logger, c, reqParam); err != nil { - return err + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } // cannot delete default supper user if reqParam.Tenant == common.DefaultAdminTenant && reqParam.Username == common.DefaultAdminUser { @@ -368,7 +368,7 @@ func ListActionV2(c echo.Context) error { if !exists { return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(fmt.Errorf("current user does not belong to any role"))) } - authority := make(map[string][]*models.ActionItem, 0) + authority := make([]models.MenuItem, 0) err = json.Unmarshal([]byte(role.Authority), &authority) if nil != err { return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) diff --git a/drivers/api/handler/v2/validation.go b/drivers/api/handler/v2/validation.go index 388c1e5cb..c93d33993 100644 --- a/drivers/api/handler/v2/validation.go +++ b/drivers/api/handler/v2/validation.go @@ -28,7 +28,7 @@ func ValidateJobV2(c echo.Context) error { logger := handler.NewLogger().Named("ValidateJobV2") reqParam := new(models.ValidateJobReqV2) if err := handler.BindAndValidate(logger, c, reqParam); err != nil { - return err + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } reqJson, err := apiJobConfigToNomadJobJson(reqParam) diff --git a/drivers/api/models/role.go b/drivers/api/models/role.go index e8e1cc00b..b7aff767c 100644 --- a/drivers/api/models/role.go +++ b/drivers/api/models/role.go @@ -43,8 +43,21 @@ type DeleteRoleReqV2 struct { type DeleteRoleRespV2 struct { BaseResp } - -type ActionItem struct { +type MenuItem struct { + Name string `json:"name"` + TextCn string `json:"text_cn"` + TextEn string `json:"text_en"` + MenuLevel int `json:"menu_level"` + MenuUrl string `json:"menu_url"` + Id int `json:"id"` + ParentId int `json:"parent_id"` + Operations []ButtonItem `json:"operations"` + AdminOnly bool `json:"admin_only"` +} + +type ButtonItem struct { Action string `json:"action"` Uri string `json:"uri"` + TextCn string `json:"text_cn"` + TextEn string `json:"text_en"` } diff --git a/drivers/api/models/user.go b/drivers/api/models/user.go index 1efd1b137..874852315 100644 --- a/drivers/api/models/user.go +++ b/drivers/api/models/user.go @@ -66,6 +66,6 @@ type CurrentUserResp struct { } type ListActionRespV2 struct { - Authority map[string][]*ActionItem `json:"authority"` + Authority []MenuItem `json:"authority"` BaseResp } diff --git a/drivers/api/route.go b/drivers/api/route.go index 6387e279d..531de33e7 100644 --- a/drivers/api/route.go +++ b/drivers/api/route.go @@ -3,6 +3,7 @@ package api import ( "encoding/json" "fmt" + "io/ioutil" "net/http" "path" "strings" @@ -83,14 +84,17 @@ func SetupApiServer(logger hclog.Logger, driverConfig *mysql.DriverConfig) (err e.POST("/v2/login", v2.LoginV2) e.POST("/v2/login/captcha", v2.CaptchaV2) e.GET("/v2/monitor/task", v2.GetTaskProgressV2) - v2Router.POST("/log/level", v2.UpdateLogLevelV2, AdminUserAllowed()) + v2Router.POST("/log/level", v2.UpdateLogLevelV2) v2Router.GET("/jobs", v2.JobListV2) v2Router.GET("/job/migration/detail", v2.GetMigrationJobDetailV2) - v2Router.POST("/job/migration", v2.CreateOrUpdateMigrationJobV2) + v2Router.POST("/job/migration/create", v2.CreateMigrationJobV2) + v2Router.POST("/job/migration/update", v2.UpdateMigrationJobV2) v2Router.GET("/job/sync/detail", v2.GetSyncJobDetailV2) - v2Router.POST("/job/sync", v2.CreateOrUpdateSyncJobV2) + v2Router.POST("/job/sync/create", v2.CreateSyncJobV2) + v2Router.POST("/job/sync/update", v2.UpdateSyncJobV2) v2Router.GET("/job/subscription/detail", v2.GetSubscriptionJobDetailV2) - v2Router.POST("/job/subscription", v2.CreateOrUpdateSubscriptionJobV2) + v2Router.POST("/job/subscription/create", v2.CreateSubscriptionJobV2) + v2Router.POST("/job/subscription/update", v2.UpdateSubscriptionJobV2) v2Router.POST("/job/pause", v2.PauseJobV2) v2Router.POST("/job/resume", v2.ResumeJobV2) v2Router.POST("/job/delete", v2.DeleteJobV2) @@ -102,18 +106,18 @@ func SetupApiServer(logger hclog.Logger, driverConfig *mysql.DriverConfig) (err v2Router.GET("/job/gtid", v2.GetJobGtidV2) v2Router.POST("/job/reverse_start", v2.ReverseStartJobV2) v2Router.POST("/job/reverse", v2.ReverseJobV2) - v2Router.GET("/user/list", v2.UserListV2, AdminUserAllowed()) - v2Router.POST("/user/create", v2.CreateUserV2, AdminUserAllowed()) + v2Router.GET("/user/list", v2.UserListV2) + v2Router.POST("/user/create", v2.CreateUserV2) v2Router.POST("/user/update", v2.UpdateUserV2) v2Router.POST("/user/reset_password", v2.ResetPasswordV2) - v2Router.POST("/user/delete", v2.DeleteUserV2, AdminUserAllowed()) - v2Router.GET("/tenant/list", v2.TenantListV2, AdminUserAllowed()) + v2Router.POST("/user/delete", v2.DeleteUserV2) + v2Router.GET("/tenant/list", v2.TenantListV2) v2Router.GET("/user/current_user", v2.GetCurrentUserV2) v2Router.GET("/user/list_action", v2.ListActionV2) - v2Router.GET("/role/list", v2.RoleListV2, AdminUserAllowed()) - v2Router.POST("/role/create", v2.CreateRoleV2, AdminUserAllowed()) - v2Router.POST("/role/delete", v2.DeleteRoleV2, AdminUserAllowed()) - v2Router.POST("/role/update", v2.UpdateRoleV2, AdminUserAllowed()) + v2Router.GET("/role/list", v2.RoleListV2) + v2Router.POST("/role/create", v2.CreateRoleV2) + v2Router.POST("/role/delete", v2.DeleteRoleV2) + v2Router.POST("/role/update", v2.UpdateRoleV2) // for pprof e.GET("/debug/pprof/*", echo.WrapHandler(http.DefaultServeMux)) @@ -205,14 +209,17 @@ func AuthFilter() echo.MiddlewareFunc { if err != nil || !exists { return echo.NewHTTPError(http.StatusForbidden) } - authority := make(map[string][]models.ActionItem, 0) + authority := make([]models.MenuItem, 0) err = json.Unmarshal([]byte(role.Authority), &authority) if err != nil { return echo.NewHTTPError(http.StatusForbidden, "please check your authority") } - for _, actionItems := range authority { - for _, item := range actionItems { - if c.Request().URL.Path == item.Uri { + for _, menuItem := range authority { + for _, buttonItem := range menuItem.Operations { + if c.Request().URL.Path == buttonItem.Uri { + if !checkForJob(c, menuItem.Name, buttonItem.Uri) { + continue + } return next(c) } } @@ -222,23 +229,64 @@ func AuthFilter() echo.MiddlewareFunc { } } -//AdminUserAllowed is a `echo` middleware, only allow admin user to access next. -func AdminUserAllowed() echo.MiddlewareFunc { - return func(next echo.HandlerFunc) echo.HandlerFunc { - return func(c echo.Context) error { - logger := handler.NewLogger().Named("AdminUserAllowed") - tenant, user := v2.GetUserName(c) - storeManager, err := common.NewStoreManager([]string{handler.ConsulAddr}, logger) - if err != nil { - return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(fmt.Errorf("consul_addr=%v; connect to consul failed: %v", handler.ConsulAddr, err))) - } - role, _, _ := storeManager.GetRole(tenant, user) - if role != nil && role.Name == common.DefaultAdminUser { - return next(c) - } - return echo.NewHTTPError(http.StatusForbidden) +func checkForJob(c echo.Context, menuType, uri string) bool { + logger := handler.NewLogger().Named("checkForJob") + logger.Error("before", "uri", uri, "menuType", menuType) + var jobType v2.DtleJobType + switch uri { + case "/v2/jobs": + reqParam := new(models.JobListReqV2) + if err := c.Bind(reqParam); nil != err { + return false + } + jobType = v2.DtleJobType(reqParam.FilterJobType) + case "/v2/job/pause": + reqParam := new(models.PauseJobReqV2) + if err := c.Bind(reqParam); nil != err { + return false + } + jobType = v2.GetJobTypeFromJobId(reqParam.JobId) + case "/v2/job/resume": + reqParam := new(models.ResumeJobReqV2) + if err := c.Bind(reqParam); nil != err { + return false } + jobType = v2.GetJobTypeFromJobId(reqParam.JobId) + case "/v2/job/delete": + reqParam := new(models.DeleteJobReqV2) + if err := c.Bind(reqParam); nil != err { + return false + } + jobType = v2.GetJobTypeFromJobId(reqParam.JobId) + case "/v2/job/reverse": + body, err := c.Request().GetBody() + if err != nil { + return false + } + bytes, err := ioutil.ReadAll(body) + if err != nil { + return false + } + reqParam := new(models.ReverseJobReq) + err = json.Unmarshal(bytes, reqParam) + if err != nil { + return false + } + jobType = v2.GetJobTypeFromJobId(reqParam.JobId) + case "/v2/job/reverse_start": + reqParam := new(models.ReverseStartReqV2) + if err := c.Bind(reqParam); nil != err { + return false + } + jobType = v2.GetJobTypeFromJobId(reqParam.JobId) + default: + return true } + logger.Error("after", "jobType", jobType, "menuType", menuType) + if string(jobType) == menuType { + return true + } + return false } var whiteList = []string{ diff --git a/drivers/mysql/common/common.go b/drivers/mysql/common/common.go index 3fb370e2b..93713af85 100644 --- a/drivers/mysql/common/common.go +++ b/drivers/mysql/common/common.go @@ -37,7 +37,7 @@ const ( DefaultAdminPwd = "admin" DefaultEncryptAdminPwd = "rAMd28u1e8j2cGfXeuef6ChGe8+SFP0b29KuJ0w9hvo2B2HbVTYaAp2E+vdBBE3KpKfo1HfOOezy8WFdk40v/xjM6Gwfhmji2czpKdfaGdnbkvChfqv2taPo8WFeRaGiaIEZ1Ygu0eKz1Yq+FOIEwNjH+clthxPqX3hiizSBbHA=" DefaultRole = "admin" - DefaultAdminAuth = "{\"sync\":[{\"action\":\"list\",\"uri\":\"/v2/jobs\"},{\"action\":\"pause\",\"uri\":\"/v2/job/pause\"},{\"action\":\"resume\",\"uri\":\"/v2/job/resume\"},{\"action\":\"reverse_start\",\"uri\":\"/v2/job/reverse_start\"},{\"action\":\"reverse\",\"uri\":\"/v2/job/reverse\"},{\"action\":\"validation\",\"uri\":\"/v2/validation/job\"},{\"action\":\"schemas\",\"uri\":\"/v2/mysql/schemas\"},{\"action\":\"columns\",\"uri\":\"/v2/mysql/columns\"},{\"action\":\"connection\",\"uri\":\"/v2/mysql/instance_connection\"},{\"action\":\"create\",\"uri\":\"/v2/job/migration\"},{\"action\":\"update\",\"uri\":\"/v2/job/migration\"},{\"action\":\"detail\",\"uri\":\"/v2/job/migration/detail\"}],\"migration\":[{\"action\":\"list\",\"uri\":\"/v2/jobs\"},{\"action\":\"pause\",\"uri\":\"/v2/job/pause\"},{\"action\":\"resume\",\"uri\":\"/v2/job/resume\"},{\"action\":\"delete\",\"uri\":\"/v2/job/delete\"},{\"action\":\"reverse_start\",\"uri\":\"/v2/job/reverse_start\"},{\"action\":\"reverse\",\"uri\":\"/v2/job/reverse\"},{\"action\":\"validation\",\"uri\":\"/v2/validation/job\"},{\"action\":\"schemas\",\"uri\":\"/v2/mysql/schemas\"},{\"action\":\"columns\",\"uri\":\"/v2/mysql/columns\"},{\"action\":\"connection\",\"uri\":\"/v2/mysql/instance_connection\"},{\"action\":\"create\",\"uri\":\"/v2/job/migration\"},{\"action\":\"update\",\"uri\":\"/v2/job/migration\"},{\"action\":\"detail\",\"uri\":\"/v2/job/migration/detail\"}],\"subscription\":[{\"action\":\"list\",\"uri\":\"/v2/jobs\"},{\"action\":\"pause\",\"uri\":\"/v2/job/pause\"},{\"action\":\"resume\",\"uri\":\"/v2/job/resume\"},{\"action\":\"delete\",\"uri\":\"/v2/job/delete\"},{\"action\":\"reverse_start\",\"uri\":\"/v2/job/reverse_start\"},{\"action\":\"reverse\",\"uri\":\"/v2/job/reverse\"},{\"action\":\"validation\",\"uri\":\"/v2/validation/job\"},{\"action\":\"schemas\",\"uri\":\"/v2/mysql/schemas\"},{\"action\":\"columns\",\"uri\":\"/v2/mysql/columns\"},{\"action\":\"connection\",\"uri\":\"/v2/mysql/instance_connection\"},{\"action\":\"create\",\"uri\":\"/v2/job/subscription\"},{\"action\":\"update\",\"uri\":\"/v2/job/migration\"},{\"action\":\"detail\",\"uri\":\"/v2/job/subscription/detail\"}],\"user\":[{\"action\":\"list\",\"uri\":\"/v2/user/list\"},{\"action\":\"list_tenant\",\"uri\":\"/v2/tenant/list\"},{\"action\":\"create\",\"uri\":\"/v2/user/create\"},{\"action\":\"update\",\"uri\":\"/v2/user/update\"},{\"action\":\"reset_pwd\",\"uri\":\"/v2/user/reset_password\"},{\"action\":\"delete\",\"uri\":\"/v2/user/delete\"},{\"action\":\"current_user\",\"uri\":\"/v2/user/current_user\"},{\"action\":\"list_action\",\"uri\":\"/v2/user/list_action\"},{\"action\":\"create\",\"uri\":\"/v2/user/create\"}],\"role\":[{\"action\":\"list\",\"uri\":\"/v2/role/list\"},{\"action\":\"create\",\"uri\":\"/v2/role/create\"},{\"action\":\"delete\",\"uri\":\"/v2/role/delete\"},{\"action\":\"update\",\"uri\":\"/v2/role/update\"}],\"node\":[{\"action\":\"list\",\"uri\":\"/v2/nodes\"}],\"task\":[{\"action\":\"monitor\",\"uri\":\"/v2/monitor/task\"}]}" + DefaultAdminAuth = "[{\"name\":\"service\",\"text_cn\":\"服务\",\"text_en\":\"service\",\"menu_level\":1,\"menu_url\":\"\",\"id\":1,\"parent_id\":0,\"operations\":[]},{\"name\":\"migration\",\"text_cn\":\"数据迁移\",\"text_en\":\"migration\",\"menu_level\":2,\"menu_url\":\"/migration\",\"id\":2,\"parent_id\":1,\"operations\":[{\"action\":\"migration-list\",\"uri\":\"/v2/jobs\",\"text_cn\":\"任务列表\",\"text_en\":\"job list\"},{\"action\":\"migration-create\",\"uri\":\"/v2/job/migration/create\",\"text_cn\":\"创建任务\",\"text_en\":\"create\"},{\"action\":\"migration-pause\",\"uri\":\"/v2/job/pause\",\"text_cn\":\"暂停任务\",\"text_en\":\"pause\"},{\"action\":\"migration-resume\",\"uri\":\"/v2/job/resume\",\"text_cn\":\"重启任务\",\"text_en\":\"resume\"},{\"action\":\"migration-delete\",\"uri\":\"/v2/job/delete\",\"text_cn\":\"销毁任务\",\"text_en\":\"delete\"},{\"action\":\"migration-reverse\",\"uri\":\"/v2/job/reverse\",\"text_cn\":\"创建反向复制任务\",\"text_en\":\"reverse\"},{\"action\":\"migration-reverse_start\",\"uri\":\"/v2/job/reverse_start\",\"text_cn\":\"启动反向任务\",\"text_en\":\"start reverse\"},{\"action\":\"migration-update\",\"uri\":\"/v2/job/migration/update\",\"text_cn\":\"修改任务\",\"text_en\":\"update\"},{\"action\":\"migration-detail\",\"uri\":\"/v2/job/migration/detail\",\"text_cn\":\"查看详情\",\"text_en\":\"detail\"}]},{\"name\":\"sync\",\"text_cn\":\"数据同步\",\"text_en\":\"sync\",\"menu_level\":2,\"menu_url\":\"/sync\",\"id\":3,\"parent_id\":1,\"operations\":[{\"action\":\"sync-list\",\"uri\":\"/v2/jobs\",\"text_cn\":\"任务列表\",\"text_en\":\"job list\"},{\"action\":\"sync-create\",\"uri\":\"/v2/job/sync/create\",\"text_cn\":\"创建任务\",\"text_en\":\"create\"},{\"action\":\"sync-pause\",\"uri\":\"/v2/job/pause\",\"text_cn\":\"暂停任务\",\"text_en\":\"pause\"},{\"action\":\"sync-resume\",\"uri\":\"/v2/job/resume\",\"text_cn\":\"重启任务\",\"text_en\":\"resume\"},{\"action\":\"sync-delete\",\"uri\":\"/v2/job/delete\",\"text_cn\":\"销毁任务\",\"text_en\":\"delete\"},{\"action\":\"sync-reverse\",\"uri\":\"/v2/job/reverse\",\"text_cn\":\"反向复制\",\"text_en\":\"reverse\"},{\"action\":\"sync-reverse_start\",\"uri\":\"/v2/job/reverse_start\",\"text_cn\":\"启动反向任务\",\"text_en\":\"reverse start\"},{\"action\":\"sync-update\",\"uri\":\"/v2/job/sync/update\",\"text_cn\":\"修改任务\",\"text_en\":\"update\"},{\"action\":\"sync-detail\",\"uri\":\"/v2/job/sync/detail\",\"text_cn\":\"查看详情\",\"text_en\":\"detail\"}]},{\"name\":\"subscription\",\"text_cn\":\"数据订阅\",\"text_en\":\"subscription\",\"menu_level\":2,\"menu_url\":\"/subscribe\",\"id\":4,\"parent_id\":1,\"operations\":[{\"action\":\"subscription-list\",\"uri\":\"/v2/jobs\",\"text_cn\":\"任务列表\",\"text_en\":\"job list\"},{\"action\":\"subscription-create\",\"uri\":\"/v2/job/subscription/create\",\"text_cn\":\"创建任务\",\"text_en\":\"create\"},{\"action\":\"subscription-pause\",\"uri\":\"/v2/job/pause\",\"text_cn\":\"暂停任务\",\"text_en\":\"pause\"},{\"action\":\"subscription-resume\",\"uri\":\"/v2/job/resume\",\"text_cn\":\"重启任务\",\"text_en\":\"resume\"},{\"action\":\"subscription-delete\",\"uri\":\"/v2/job/delete\",\"text_cn\":\"销毁任务\",\"text_en\":\"delete\"},{\"action\":\"subscription-update\",\"uri\":\"/v2/job/subscription/update\",\"text_cn\":\"修改任务\",\"text_en\":\"update\"},{\"action\":\"subscription-detail\",\"uri\":\"/v2/job/subscription/detail\",\"text_cn\":\"查看详情\",\"text_en\":\"detail\"}]},{\"name\":\"platform\",\"text_cn\":\"平台管理\",\"text_en\":\"platform\",\"menu_level\":1,\"menu_url\":\"\",\"id\":5,\"parent_id\":0,\"operations\":[]},{\"name\":\"node\",\"text_cn\":\"DTLE节点\",\"text_en\":\"dtle nodes\",\"menu_level\":2,\"menu_url\":\"/node\",\"id\":7,\"parent_id\":5,\"operations\":[{\"action\":\"node-list\",\"uri\":\"/v2/nodes\",\"text_cn\":\"获取节点列表\",\"text_en\":\"get node list\"}]},{\"name\":\"users\",\"admin_only\":true,\"text_cn\":\"用户管理\",\"text_en\":\"user manage\",\"menu_level\":2,\"menu_url\":\"/users\",\"id\":6,\"parent_id\":5,\"operations\":[{\"action\":\"user-list\",\"uri\":\"/v2/user/list\",\"text_cn\":\"查看用户列表\",\"text_en\":\"user list\"},{\"action\":\"user-list_tenant\",\"uri\":\"/v2/tenant/list\",\"text_cn\":\"获取租户列表\",\"text_en\":\"get tenants\"},{\"action\":\"user-create\",\"uri\":\"/v2/user/create\",\"text_cn\":\"创建用户\",\"text_en\":\"create user\"},{\"action\":\"user-delete\",\"uri\":\"/v2/user/delete\",\"text_cn\":\"删除用户\",\"text_en\":\"delete\"},{\"action\":\"user-update\",\"uri\":\"/v2/user/update\",\"text_cn\":\"修改用户\",\"text_en\":\"update user\"}]},{\"name\":\"auth\",\"admin_only\":true,\"text_cn\":\"权限配置\",\"text_en\":\"rights profile\",\"menu_level\":2,\"menu_url\":\"/auth\",\"id\":8,\"parent_id\":5,\"operations\":[{\"action\":\"auth-list\",\"uri\":\"/v2/role/list\",\"text_cn\":\"查看角色列表\",\"text_en\":\"role list\"},{\"action\":\"auth-create\",\"uri\":\"/v2/role/create\",\"text_cn\":\"创建角色\",\"text_en\":\"create\"},{\"action\":\"auth-delete\",\"uri\":\"/v2/role/delete\",\"text_cn\":\"删除角色\",\"text_en\":\"delete\"},{\"action\":\"auth-update\",\"uri\":\"/v2/role/update\",\"text_cn\":\"修改角色\",\"text_en\":\"update\"}]}]" // TODO: Using configuration to set jwt secret JWTSecret = "secret" ) diff --git a/drivers/mysql/common/store.go b/drivers/mysql/common/store.go index efa6ff351..f63980477 100644 --- a/drivers/mysql/common/store.go +++ b/drivers/mysql/common/store.go @@ -292,6 +292,12 @@ func (sm *StoreManager) GetJobInfo(jobId string) (*JobListItemV2, error) { return job, nil } +func (sm *StoreManager) CheckJobExists(jobId string) bool { + key := fmt.Sprintf("dtleJobList/%v", jobId) + exists, _ := sm.consulStore.Exists(key) + return exists +} + func (sm *StoreManager) GetJobStatus(jobId string) (string, error) { jobInfo, err := sm.GetJobInfo(jobId) if err == store.ErrKeyNotFound { From cdfffc10296b5f143d0f769c08640198098a049c Mon Sep 17 00:00:00 2001 From: yangzhongjiao <451773851@qq.com> Date: Mon, 23 Aug 2021 10:42:38 +0800 Subject: [PATCH 3/5] split api --- drivers/api/docs/docs.go | 521 ++++++++++++++++++++++++++++---- drivers/api/docs/swagger.json | 521 ++++++++++++++++++++++++++++---- drivers/api/docs/swagger.yaml | 348 +++++++++++++++++---- drivers/api/handler/v2/auth.go | 7 +- drivers/api/handler/v2/job.go | 216 +++++++++++-- drivers/api/handler/v2/login.go | 7 +- drivers/api/handler/v2/user.go | 2 +- drivers/api/route.go | 91 ++---- drivers/mysql/common/common.go | 6 +- 9 files changed, 1431 insertions(+), 288 deletions(-) diff --git a/drivers/api/docs/docs.go b/drivers/api/docs/docs.go index 91f6696c8..11c3317eb 100644 --- a/drivers/api/docs/docs.go +++ b/drivers/api/docs/docs.go @@ -24,27 +24,24 @@ var doc = `{ "host": "{{.Host}}", "basePath": "{{.BasePath}}", "paths": { - "/v2/job/delete": { - "post": { + "/v2/job/gtid": { + "get": { "security": [ { "ApiKeyAuth": [] } ], - "description": "delete job.", - "consumes": [ - "application/x-www-form-urlencoded" - ], + "description": "get src task current gtid.", "tags": [ "job" ], - "operationId": "DeleteJobV2", + "operationId": "GetJobGtidV2", "parameters": [ { "type": "string", "description": "job id", "name": "job_id", - "in": "formData", + "in": "query", "required": true } ], @@ -52,74 +49,77 @@ var doc = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/models.DeleteJobRespV2" + "$ref": "#/definitions/models.JobGtidResp" } } } } }, - "/v2/job/gtid": { - "get": { + "/v2/job/migration/create": { + "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "get src task current gtid.", + "description": "create migration job.", + "consumes": [ + "application/json" + ], "tags": [ "job" ], - "operationId": "GetJobGtidV2", + "operationId": "CreateMigrationJobV2", "parameters": [ { - "type": "string", - "description": "job id", - "name": "job_id", - "in": "query", - "required": true + "description": "migration job config", + "name": "migration_job_config", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.CreateOrUpdateMysqlToMysqlJobParamV2" + } } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/models.JobGtidResp" + "$ref": "#/definitions/models.CreateOrUpdateMysqlToMysqlJobRespV2" } } } } }, - "/v2/job/migration/create": { + "/v2/job/migration/delete": { "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "create migration job.", + "description": "delete migration job.", "consumes": [ - "application/json" + "application/x-www-form-urlencoded" ], "tags": [ "job" ], - "operationId": "CreateMigrationJobV2", + "operationId": "DeleteMigrationJobV2", "parameters": [ { - "description": "migration job config", - "name": "migration_job_config", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/models.CreateOrUpdateMysqlToMysqlJobParamV2" - } + "type": "string", + "description": "job id", + "name": "job_id", + "in": "formData", + "required": true } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/models.CreateOrUpdateMysqlToMysqlJobRespV2" + "$ref": "#/definitions/models.DeleteJobRespV2" } } } @@ -156,57 +156,55 @@ var doc = `{ } } }, - "/v2/job/migration/update": { + "/v2/job/migration/pause": { "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "update migration job.", + "description": "pause migration job.", "consumes": [ - "application/json" + "application/x-www-form-urlencoded" ], "tags": [ "job" ], - "operationId": "UpdateMigrationJobV2", + "operationId": "PauseMigrationJobV2", "parameters": [ { - "description": "migration job config", - "name": "migration_job_config", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/models.CreateOrUpdateMysqlToMysqlJobParamV2" - } + "type": "string", + "description": "job id", + "name": "job_id", + "in": "formData", + "required": true } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/models.CreateOrUpdateMysqlToMysqlJobRespV2" + "$ref": "#/definitions/models.PauseJobRespV2" } } } } }, - "/v2/job/pause": { + "/v2/job/migration/resume": { "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "pause job.", + "description": "resume migration job.", "consumes": [ "application/x-www-form-urlencoded" ], "tags": [ "job" ], - "operationId": "PauseJobV2", + "operationId": "ResumeMigrationJobV2", "parameters": [ { "type": "string", @@ -220,27 +218,64 @@ var doc = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/models.PauseJobRespV2" + "$ref": "#/definitions/models.ResumeJobRespV2" } } } } }, - "/v2/job/resume": { + "/v2/job/migration/reverse": { "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "resume job.", + "description": "reverse migration Job", + "consumes": [ + "application/json" + ], + "tags": [ + "job" + ], + "operationId": "ReverseMigrationJobV2", + "parameters": [ + { + "description": "reverse config config", + "name": "reverse_config", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.ReverseJobReq" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.ReverseJobResp" + } + } + } + } + }, + "/v2/job/migration/reverse_start": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Start Reverse Job.", "consumes": [ "application/x-www-form-urlencoded" ], "tags": [ "job" ], - "operationId": "ResumeJobV2", + "summary": "start reverse-init job", + "operationId": "ReverseStartMigrationJobV2", "parameters": [ { "type": "string", @@ -254,35 +289,35 @@ var doc = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/models.ResumeJobRespV2" + "$ref": "#/definitions/models.ReverseStartRespV2" } } } } }, - "/v2/job/reverse": { + "/v2/job/migration/update": { "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "returnJob", + "description": "update migration job.", "consumes": [ "application/json" ], "tags": [ "job" ], - "operationId": "ReverseJobV2", + "operationId": "UpdateMigrationJobV2", "parameters": [ { - "description": "reverse config config", - "name": "reverse_config", + "description": "migration job config", + "name": "migration_job_config", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/models.ReverseJobReq" + "$ref": "#/definitions/models.CreateOrUpdateMysqlToMysqlJobParamV2" } } ], @@ -290,7 +325,7 @@ var doc = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/models.ReverseJobResp" + "$ref": "#/definitions/models.CreateOrUpdateMysqlToMysqlJobRespV2" } } } @@ -403,6 +438,40 @@ var doc = `{ } } }, + "/v2/job/subscription/delete": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "delete subscription job.", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "tags": [ + "job" + ], + "operationId": "DeleteSubscriptionJobV2", + "parameters": [ + { + "type": "string", + "description": "job id", + "name": "job_id", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.DeleteJobRespV2" + } + } + } + } + }, "/v2/job/subscription/detail": { "get": { "security": [ @@ -434,6 +503,74 @@ var doc = `{ } } }, + "/v2/job/subscription/pause": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "pause subscription job.", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "tags": [ + "job" + ], + "operationId": "PauseSubscriptionJobV2", + "parameters": [ + { + "type": "string", + "description": "job id", + "name": "job_id", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.PauseJobRespV2" + } + } + } + } + }, + "/v2/job/subscription/resume": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "resume subscription job.", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "tags": [ + "job" + ], + "operationId": "ResumeSubscriptionJobV2", + "parameters": [ + { + "type": "string", + "description": "job id", + "name": "job_id", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.ResumeJobRespV2" + } + } + } + } + }, "/v2/job/sync/create": { "post": { "security": [ @@ -470,6 +607,40 @@ var doc = `{ } } }, + "/v2/job/sync/delete": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "delete sync job.", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "tags": [ + "job" + ], + "operationId": "DeleteSyncJobV2", + "parameters": [ + { + "type": "string", + "description": "job id", + "name": "job_id", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.DeleteJobRespV2" + } + } + } + } + }, "/v2/job/sync/detail": { "get": { "security": [ @@ -501,6 +672,145 @@ var doc = `{ } } }, + "/v2/job/sync/pause": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "pause sync job.", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "tags": [ + "job" + ], + "operationId": "PauseSyncJobV2", + "parameters": [ + { + "type": "string", + "description": "job id", + "name": "job_id", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.PauseJobRespV2" + } + } + } + } + }, + "/v2/job/sync/resume": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "resume sync job.", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "tags": [ + "job" + ], + "operationId": "ResumeSyncJobV2", + "parameters": [ + { + "type": "string", + "description": "job id", + "name": "job_id", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.ResumeJobRespV2" + } + } + } + } + }, + "/v2/job/sync/reverse": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "reverse sync Job", + "consumes": [ + "application/json" + ], + "tags": [ + "job" + ], + "operationId": "ReverseSyncJobV2", + "parameters": [ + { + "description": "reverse config config", + "name": "reverse_config", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.ReverseJobReq" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.ReverseJobResp" + } + } + } + } + }, + "/v2/job/sync/reverse_start": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Start Reverse Job.", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "tags": [ + "job" + ], + "summary": "start reverse-init job", + "operationId": "ReverseStartSyncJobV2", + "parameters": [ + { + "type": "string", + "description": "job id", + "name": "job_id", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.ReverseStartRespV2" + } + } + } + } + }, "/v2/job/sync/update": { "post": { "security": [ @@ -537,7 +847,7 @@ var doc = `{ } } }, - "/v2/jobs": { + "/v2/jobs/migration": { "get": { "security": [ { @@ -548,19 +858,100 @@ var doc = `{ "tags": [ "job" ], - "operationId": "JobListV2", + "operationId": "MigrationJobListV2", "parameters": [ + { + "type": "string", + "description": "filter job id", + "name": "filter_job_id", + "in": "query" + }, + { + "type": "string", + "description": "filter job status", + "name": "filter_job_status", + "in": "query" + }, { "enum": [ - "migration", - "sync", - "subscription" + "job_create_time" ], "type": "string", - "description": "filter job type", - "name": "filter_job_type", + "default": "job_create_time", + "description": "order by", + "name": "order_by", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.JobListRespV2" + } + } + } + } + }, + "/v2/jobs/subscription": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "get subscription job list.", + "tags": [ + "job" + ], + "operationId": "SubscriptionJobListV2", + "parameters": [ + { + "type": "string", + "description": "filter job id", + "name": "filter_job_id", + "in": "query" + }, + { + "type": "string", + "description": "filter job status", + "name": "filter_job_status", "in": "query" }, + { + "enum": [ + "job_create_time" + ], + "type": "string", + "default": "job_create_time", + "description": "order by", + "name": "order_by", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.JobListRespV2" + } + } + } + } + }, + "/v2/jobs/sync": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "get sync job list.", + "tags": [ + "job" + ], + "operationId": "SyncJobListV2", + "parameters": [ { "type": "string", "description": "filter job id", diff --git a/drivers/api/docs/swagger.json b/drivers/api/docs/swagger.json index 8f2769c18..d7f3c2664 100644 --- a/drivers/api/docs/swagger.json +++ b/drivers/api/docs/swagger.json @@ -8,27 +8,24 @@ }, "basePath": "/", "paths": { - "/v2/job/delete": { - "post": { + "/v2/job/gtid": { + "get": { "security": [ { "ApiKeyAuth": [] } ], - "description": "delete job.", - "consumes": [ - "application/x-www-form-urlencoded" - ], + "description": "get src task current gtid.", "tags": [ "job" ], - "operationId": "DeleteJobV2", + "operationId": "GetJobGtidV2", "parameters": [ { "type": "string", "description": "job id", "name": "job_id", - "in": "formData", + "in": "query", "required": true } ], @@ -36,74 +33,77 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/models.DeleteJobRespV2" + "$ref": "#/definitions/models.JobGtidResp" } } } } }, - "/v2/job/gtid": { - "get": { + "/v2/job/migration/create": { + "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "get src task current gtid.", + "description": "create migration job.", + "consumes": [ + "application/json" + ], "tags": [ "job" ], - "operationId": "GetJobGtidV2", + "operationId": "CreateMigrationJobV2", "parameters": [ { - "type": "string", - "description": "job id", - "name": "job_id", - "in": "query", - "required": true + "description": "migration job config", + "name": "migration_job_config", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.CreateOrUpdateMysqlToMysqlJobParamV2" + } } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/models.JobGtidResp" + "$ref": "#/definitions/models.CreateOrUpdateMysqlToMysqlJobRespV2" } } } } }, - "/v2/job/migration/create": { + "/v2/job/migration/delete": { "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "create migration job.", + "description": "delete migration job.", "consumes": [ - "application/json" + "application/x-www-form-urlencoded" ], "tags": [ "job" ], - "operationId": "CreateMigrationJobV2", + "operationId": "DeleteMigrationJobV2", "parameters": [ { - "description": "migration job config", - "name": "migration_job_config", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/models.CreateOrUpdateMysqlToMysqlJobParamV2" - } + "type": "string", + "description": "job id", + "name": "job_id", + "in": "formData", + "required": true } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/models.CreateOrUpdateMysqlToMysqlJobRespV2" + "$ref": "#/definitions/models.DeleteJobRespV2" } } } @@ -140,57 +140,55 @@ } } }, - "/v2/job/migration/update": { + "/v2/job/migration/pause": { "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "update migration job.", + "description": "pause migration job.", "consumes": [ - "application/json" + "application/x-www-form-urlencoded" ], "tags": [ "job" ], - "operationId": "UpdateMigrationJobV2", + "operationId": "PauseMigrationJobV2", "parameters": [ { - "description": "migration job config", - "name": "migration_job_config", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/models.CreateOrUpdateMysqlToMysqlJobParamV2" - } + "type": "string", + "description": "job id", + "name": "job_id", + "in": "formData", + "required": true } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/models.CreateOrUpdateMysqlToMysqlJobRespV2" + "$ref": "#/definitions/models.PauseJobRespV2" } } } } }, - "/v2/job/pause": { + "/v2/job/migration/resume": { "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "pause job.", + "description": "resume migration job.", "consumes": [ "application/x-www-form-urlencoded" ], "tags": [ "job" ], - "operationId": "PauseJobV2", + "operationId": "ResumeMigrationJobV2", "parameters": [ { "type": "string", @@ -204,27 +202,64 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/models.PauseJobRespV2" + "$ref": "#/definitions/models.ResumeJobRespV2" } } } } }, - "/v2/job/resume": { + "/v2/job/migration/reverse": { "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "resume job.", + "description": "reverse migration Job", + "consumes": [ + "application/json" + ], + "tags": [ + "job" + ], + "operationId": "ReverseMigrationJobV2", + "parameters": [ + { + "description": "reverse config config", + "name": "reverse_config", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.ReverseJobReq" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.ReverseJobResp" + } + } + } + } + }, + "/v2/job/migration/reverse_start": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Start Reverse Job.", "consumes": [ "application/x-www-form-urlencoded" ], "tags": [ "job" ], - "operationId": "ResumeJobV2", + "summary": "start reverse-init job", + "operationId": "ReverseStartMigrationJobV2", "parameters": [ { "type": "string", @@ -238,35 +273,35 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/models.ResumeJobRespV2" + "$ref": "#/definitions/models.ReverseStartRespV2" } } } } }, - "/v2/job/reverse": { + "/v2/job/migration/update": { "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "returnJob", + "description": "update migration job.", "consumes": [ "application/json" ], "tags": [ "job" ], - "operationId": "ReverseJobV2", + "operationId": "UpdateMigrationJobV2", "parameters": [ { - "description": "reverse config config", - "name": "reverse_config", + "description": "migration job config", + "name": "migration_job_config", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/models.ReverseJobReq" + "$ref": "#/definitions/models.CreateOrUpdateMysqlToMysqlJobParamV2" } } ], @@ -274,7 +309,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/models.ReverseJobResp" + "$ref": "#/definitions/models.CreateOrUpdateMysqlToMysqlJobRespV2" } } } @@ -387,6 +422,40 @@ } } }, + "/v2/job/subscription/delete": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "delete subscription job.", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "tags": [ + "job" + ], + "operationId": "DeleteSubscriptionJobV2", + "parameters": [ + { + "type": "string", + "description": "job id", + "name": "job_id", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.DeleteJobRespV2" + } + } + } + } + }, "/v2/job/subscription/detail": { "get": { "security": [ @@ -418,6 +487,74 @@ } } }, + "/v2/job/subscription/pause": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "pause subscription job.", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "tags": [ + "job" + ], + "operationId": "PauseSubscriptionJobV2", + "parameters": [ + { + "type": "string", + "description": "job id", + "name": "job_id", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.PauseJobRespV2" + } + } + } + } + }, + "/v2/job/subscription/resume": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "resume subscription job.", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "tags": [ + "job" + ], + "operationId": "ResumeSubscriptionJobV2", + "parameters": [ + { + "type": "string", + "description": "job id", + "name": "job_id", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.ResumeJobRespV2" + } + } + } + } + }, "/v2/job/sync/create": { "post": { "security": [ @@ -454,6 +591,40 @@ } } }, + "/v2/job/sync/delete": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "delete sync job.", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "tags": [ + "job" + ], + "operationId": "DeleteSyncJobV2", + "parameters": [ + { + "type": "string", + "description": "job id", + "name": "job_id", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.DeleteJobRespV2" + } + } + } + } + }, "/v2/job/sync/detail": { "get": { "security": [ @@ -485,6 +656,145 @@ } } }, + "/v2/job/sync/pause": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "pause sync job.", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "tags": [ + "job" + ], + "operationId": "PauseSyncJobV2", + "parameters": [ + { + "type": "string", + "description": "job id", + "name": "job_id", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.PauseJobRespV2" + } + } + } + } + }, + "/v2/job/sync/resume": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "resume sync job.", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "tags": [ + "job" + ], + "operationId": "ResumeSyncJobV2", + "parameters": [ + { + "type": "string", + "description": "job id", + "name": "job_id", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.ResumeJobRespV2" + } + } + } + } + }, + "/v2/job/sync/reverse": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "reverse sync Job", + "consumes": [ + "application/json" + ], + "tags": [ + "job" + ], + "operationId": "ReverseSyncJobV2", + "parameters": [ + { + "description": "reverse config config", + "name": "reverse_config", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.ReverseJobReq" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.ReverseJobResp" + } + } + } + } + }, + "/v2/job/sync/reverse_start": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Start Reverse Job.", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "tags": [ + "job" + ], + "summary": "start reverse-init job", + "operationId": "ReverseStartSyncJobV2", + "parameters": [ + { + "type": "string", + "description": "job id", + "name": "job_id", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.ReverseStartRespV2" + } + } + } + } + }, "/v2/job/sync/update": { "post": { "security": [ @@ -521,7 +831,7 @@ } } }, - "/v2/jobs": { + "/v2/jobs/migration": { "get": { "security": [ { @@ -532,19 +842,100 @@ "tags": [ "job" ], - "operationId": "JobListV2", + "operationId": "MigrationJobListV2", "parameters": [ + { + "type": "string", + "description": "filter job id", + "name": "filter_job_id", + "in": "query" + }, + { + "type": "string", + "description": "filter job status", + "name": "filter_job_status", + "in": "query" + }, { "enum": [ - "migration", - "sync", - "subscription" + "job_create_time" ], "type": "string", - "description": "filter job type", - "name": "filter_job_type", + "default": "job_create_time", + "description": "order by", + "name": "order_by", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.JobListRespV2" + } + } + } + } + }, + "/v2/jobs/subscription": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "get subscription job list.", + "tags": [ + "job" + ], + "operationId": "SubscriptionJobListV2", + "parameters": [ + { + "type": "string", + "description": "filter job id", + "name": "filter_job_id", + "in": "query" + }, + { + "type": "string", + "description": "filter job status", + "name": "filter_job_status", "in": "query" }, + { + "enum": [ + "job_create_time" + ], + "type": "string", + "default": "job_create_time", + "description": "order by", + "name": "order_by", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.JobListRespV2" + } + } + } + } + }, + "/v2/jobs/sync": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "get sync job list.", + "tags": [ + "job" + ], + "operationId": "SyncJobListV2", + "parameters": [ { "type": "string", "description": "filter job id", diff --git a/drivers/api/docs/swagger.yaml b/drivers/api/docs/swagger.yaml index d5d0b7ccb..03a8ff24b 100644 --- a/drivers/api/docs/swagger.yaml +++ b/drivers/api/docs/swagger.yaml @@ -973,27 +973,6 @@ info: title: dtle API Docs version: "2.0" paths: - /v2/job/delete: - post: - consumes: - - application/x-www-form-urlencoded - description: delete job. - operationId: DeleteJobV2 - parameters: - - description: job id - in: formData - name: job_id - required: true - type: string - responses: - "200": - description: OK - schema: - $ref: '#/definitions/models.DeleteJobRespV2' - security: - - ApiKeyAuth: [] - tags: - - job /v2/job/gtid: get: description: get src task current gtid. @@ -1035,6 +1014,27 @@ paths: - ApiKeyAuth: [] tags: - job + /v2/job/migration/delete: + post: + consumes: + - application/x-www-form-urlencoded + description: delete migration job. + operationId: DeleteMigrationJobV2 + parameters: + - description: job id + in: formData + name: job_id + required: true + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.DeleteJobRespV2' + security: + - ApiKeyAuth: [] + tags: + - job /v2/job/migration/detail: get: description: get migration job detail. @@ -1054,34 +1054,33 @@ paths: - ApiKeyAuth: [] tags: - job - /v2/job/migration/update: + /v2/job/migration/pause: post: consumes: - - application/json - description: update migration job. - operationId: UpdateMigrationJobV2 + - application/x-www-form-urlencoded + description: pause migration job. + operationId: PauseMigrationJobV2 parameters: - - description: migration job config - in: body - name: migration_job_config + - description: job id + in: formData + name: job_id required: true - schema: - $ref: '#/definitions/models.CreateOrUpdateMysqlToMysqlJobParamV2' + type: string responses: "200": description: OK schema: - $ref: '#/definitions/models.CreateOrUpdateMysqlToMysqlJobRespV2' + $ref: '#/definitions/models.PauseJobRespV2' security: - ApiKeyAuth: [] tags: - job - /v2/job/pause: + /v2/job/migration/resume: post: consumes: - application/x-www-form-urlencoded - description: pause job. - operationId: PauseJobV2 + description: resume migration job. + operationId: ResumeMigrationJobV2 parameters: - description: job id in: formData @@ -1092,17 +1091,39 @@ paths: "200": description: OK schema: - $ref: '#/definitions/models.PauseJobRespV2' + $ref: '#/definitions/models.ResumeJobRespV2' security: - ApiKeyAuth: [] tags: - job - /v2/job/resume: + /v2/job/migration/reverse: + post: + consumes: + - application/json + description: reverse migration Job + operationId: ReverseMigrationJobV2 + parameters: + - description: reverse config config + in: body + name: reverse_config + required: true + schema: + $ref: '#/definitions/models.ReverseJobReq' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.ReverseJobResp' + security: + - ApiKeyAuth: [] + tags: + - job + /v2/job/migration/reverse_start: post: consumes: - application/x-www-form-urlencoded - description: resume job. - operationId: ResumeJobV2 + description: Start Reverse Job. + operationId: ReverseStartMigrationJobV2 parameters: - description: job id in: formData @@ -1113,29 +1134,30 @@ paths: "200": description: OK schema: - $ref: '#/definitions/models.ResumeJobRespV2' + $ref: '#/definitions/models.ReverseStartRespV2' security: - ApiKeyAuth: [] + summary: start reverse-init job tags: - job - /v2/job/reverse: + /v2/job/migration/update: post: consumes: - application/json - description: returnJob - operationId: ReverseJobV2 + description: update migration job. + operationId: UpdateMigrationJobV2 parameters: - - description: reverse config config + - description: migration job config in: body - name: reverse_config + name: migration_job_config required: true schema: - $ref: '#/definitions/models.ReverseJobReq' + $ref: '#/definitions/models.CreateOrUpdateMysqlToMysqlJobParamV2' responses: "200": description: OK schema: - $ref: '#/definitions/models.ReverseJobResp' + $ref: '#/definitions/models.CreateOrUpdateMysqlToMysqlJobRespV2' security: - ApiKeyAuth: [] tags: @@ -1206,6 +1228,27 @@ paths: - ApiKeyAuth: [] tags: - job + /v2/job/subscription/delete: + post: + consumes: + - application/x-www-form-urlencoded + description: delete subscription job. + operationId: DeleteSubscriptionJobV2 + parameters: + - description: job id + in: formData + name: job_id + required: true + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.DeleteJobRespV2' + security: + - ApiKeyAuth: [] + tags: + - job /v2/job/subscription/detail: get: description: get subscription job detail. @@ -1225,6 +1268,48 @@ paths: - ApiKeyAuth: [] tags: - job + /v2/job/subscription/pause: + post: + consumes: + - application/x-www-form-urlencoded + description: pause subscription job. + operationId: PauseSubscriptionJobV2 + parameters: + - description: job id + in: formData + name: job_id + required: true + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.PauseJobRespV2' + security: + - ApiKeyAuth: [] + tags: + - job + /v2/job/subscription/resume: + post: + consumes: + - application/x-www-form-urlencoded + description: resume subscription job. + operationId: ResumeSubscriptionJobV2 + parameters: + - description: job id + in: formData + name: job_id + required: true + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.ResumeJobRespV2' + security: + - ApiKeyAuth: [] + tags: + - job /v2/job/sync/create: post: consumes: @@ -1247,6 +1332,27 @@ paths: - ApiKeyAuth: [] tags: - job + /v2/job/sync/delete: + post: + consumes: + - application/x-www-form-urlencoded + description: delete sync job. + operationId: DeleteSyncJobV2 + parameters: + - description: job id + in: formData + name: job_id + required: true + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.DeleteJobRespV2' + security: + - ApiKeyAuth: [] + tags: + - job /v2/job/sync/detail: get: description: get sync job detail. @@ -1266,6 +1372,92 @@ paths: - ApiKeyAuth: [] tags: - job + /v2/job/sync/pause: + post: + consumes: + - application/x-www-form-urlencoded + description: pause sync job. + operationId: PauseSyncJobV2 + parameters: + - description: job id + in: formData + name: job_id + required: true + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.PauseJobRespV2' + security: + - ApiKeyAuth: [] + tags: + - job + /v2/job/sync/resume: + post: + consumes: + - application/x-www-form-urlencoded + description: resume sync job. + operationId: ResumeSyncJobV2 + parameters: + - description: job id + in: formData + name: job_id + required: true + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.ResumeJobRespV2' + security: + - ApiKeyAuth: [] + tags: + - job + /v2/job/sync/reverse: + post: + consumes: + - application/json + description: reverse sync Job + operationId: ReverseSyncJobV2 + parameters: + - description: reverse config config + in: body + name: reverse_config + required: true + schema: + $ref: '#/definitions/models.ReverseJobReq' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.ReverseJobResp' + security: + - ApiKeyAuth: [] + tags: + - job + /v2/job/sync/reverse_start: + post: + consumes: + - application/x-www-form-urlencoded + description: Start Reverse Job. + operationId: ReverseStartSyncJobV2 + parameters: + - description: job id + in: formData + name: job_id + required: true + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.ReverseStartRespV2' + security: + - ApiKeyAuth: [] + summary: start reverse-init job + tags: + - job /v2/job/sync/update: post: consumes: @@ -1288,19 +1480,69 @@ paths: - ApiKeyAuth: [] tags: - job - /v2/jobs: + /v2/jobs/migration: get: description: get job list. - operationId: JobListV2 + operationId: MigrationJobListV2 + parameters: + - description: filter job id + in: query + name: filter_job_id + type: string + - description: filter job status + in: query + name: filter_job_status + type: string + - default: job_create_time + description: order by + enum: + - job_create_time + in: query + name: order_by + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.JobListRespV2' + security: + - ApiKeyAuth: [] + tags: + - job + /v2/jobs/subscription: + get: + description: get subscription job list. + operationId: SubscriptionJobListV2 parameters: - - description: filter job type + - description: filter job id + in: query + name: filter_job_id + type: string + - description: filter job status + in: query + name: filter_job_status + type: string + - default: job_create_time + description: order by enum: - - migration - - sync - - subscription + - job_create_time in: query - name: filter_job_type + name: order_by type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.JobListRespV2' + security: + - ApiKeyAuth: [] + tags: + - job + /v2/jobs/sync: + get: + description: get sync job list. + operationId: SyncJobListV2 + parameters: - description: filter job id in: query name: filter_job_id diff --git a/drivers/api/handler/v2/auth.go b/drivers/api/handler/v2/auth.go index 46c24927c..7d607ff1f 100644 --- a/drivers/api/handler/v2/auth.go +++ b/drivers/api/handler/v2/auth.go @@ -60,10 +60,7 @@ func (b *BlackList) blackListExist(key string) (int, bool) { } // validate current user in blacklist and update blacklist -func ValidatePassword(user, operation, currentPwd, verifiedPwd string) error { - if leftMinute, exist := BL.blackListExist(fmt.Sprintf("%s:%s", user, operation)); exist { - return fmt.Errorf("the password cannot be changed temporarily, please try again after %v minute", leftMinute) - } +func ValidatePassword(blackListKey, currentPwd, verifiedPwd string) error { realCurrentPwd, err := handler.DecryptPasswordSupportNoRsaKey(currentPwd, g.RsaPrivateKey) if err != nil { return fmt.Errorf("decrypt current password err") @@ -73,7 +70,7 @@ func ValidatePassword(user, operation, currentPwd, verifiedPwd string) error { return fmt.Errorf("decrypt verified password err") } if realCurrentPwd != realVerifiedPwd { - BL.setBlackList(fmt.Sprintf("%s:%s", user, operation), time.Minute*30) + BL.setBlackList(blackListKey, time.Minute*30) return fmt.Errorf("user or password is wrong") } return nil diff --git a/drivers/api/handler/v2/job.go b/drivers/api/handler/v2/job.go index 943b6763d..2a5448084 100644 --- a/drivers/api/handler/v2/job.go +++ b/drivers/api/handler/v2/job.go @@ -28,17 +28,46 @@ import ( "github.com/labstack/echo/v4" ) -// @Id JobListV2 +// @Id MigrationJobListV2 // @Description get job list. // @Tags job // @Success 200 {object} models.JobListRespV2 // @Security ApiKeyAuth -// @Param filter_job_type query string false "filter job type" Enums(migration,sync,subscription) // @Param filter_job_id query string false "filter job id" // @Param filter_job_status query string false "filter job status" // @Param order_by query string false "order by" default(job_create_time) Enums(job_create_time) -// @Router /v2/jobs [get] -func JobListV2(c echo.Context) error { +// @Router /v2/jobs/migration [get] +func MigrationJobListV2(c echo.Context) error { + return JobListV2(c, DtleJobTypeMigration) +} + +// @Id SyncJobListV2 +// @Description get sync job list. +// @Tags job +// @Success 200 {object} models.JobListRespV2 +// @Security ApiKeyAuth +// @Param filter_job_id query string false "filter job id" +// @Param filter_job_status query string false "filter job status" +// @Param order_by query string false "order by" default(job_create_time) Enums(job_create_time) +// @Router /v2/jobs/sync [get] +func SyncJobListV2(c echo.Context) error { + return JobListV2(c, DtleJobTypeSync) +} + +// @Id SubscriptionJobListV2 +// @Description get subscription job list. +// @Tags job +// @Success 200 {object} models.JobListRespV2 +// @Security ApiKeyAuth +// @Param filter_job_id query string false "filter job id" +// @Param filter_job_status query string false "filter job status" +// @Param order_by query string false "order by" default(job_create_time) Enums(job_create_time) +// @Router /v2/jobs/subscription [get] +func SubscriptionJobListV2(c echo.Context) error { + return JobListV2(c, DtleJobTypeSubscription) +} + +func JobListV2(c echo.Context, filterJobType DtleJobType) error { logger := handler.NewLogger().Named("JobListV2") reqParam := new(models.JobListReqV2) if err := handler.BindAndValidate(logger, c, reqParam); err != nil { @@ -67,7 +96,7 @@ func JobListV2(c echo.Context) error { jobs := make([]common.JobListItemV2, 0) for _, consulJob := range jobList { jobType := GetJobTypeFromJobId(consulJob.JobId) - if "" != reqParam.FilterJobType && reqParam.FilterJobType != string(jobType) { + if filterJobType != jobType { continue } if !userHasAccess(storeManager, consulJob.User, user) { @@ -1140,21 +1169,51 @@ func buildKafkaDestTaskDetail(taskName string, internalTaskKafkaConfig common.Ka return destTaskDetail } -// @Id PauseJobV2 -// @Description pause job. +// @Id PauseMigrationJobV2 +// @Description pause migration job. +// @Tags job +// @accept application/x-www-form-urlencoded +// @Security ApiKeyAuth +// @Param job_id formData string true "job id" +// @Success 200 {object} models.PauseJobRespV2 +// @Router /v2/job/migration/pause [post] +func PauseMigrationJobV2(c echo.Context) error { + return PauseJobV2(c, DtleJobTypeMigration) +} + +// @Id PauseSyncJobV2 +// @Description pause sync job. // @Tags job // @accept application/x-www-form-urlencoded // @Security ApiKeyAuth // @Param job_id formData string true "job id" // @Success 200 {object} models.PauseJobRespV2 -// @Router /v2/job/pause [post] -func PauseJobV2(c echo.Context) error { +// @Router /v2/job/sync/pause [post] +func PauseSyncJobV2(c echo.Context) error { + return PauseJobV2(c, DtleJobTypeSync) +} + +// @Id PauseSubscriptionJobV2 +// @Description pause subscription job. +// @Tags job +// @accept application/x-www-form-urlencoded +// @Security ApiKeyAuth +// @Param job_id formData string true "job id" +// @Success 200 {object} models.PauseJobRespV2 +// @Router /v2/job/subscription/pause [post] +func PauseSubscriptionJobV2(c echo.Context) error { + return PauseJobV2(c, DtleJobTypeSubscription) +} + +func PauseJobV2(c echo.Context, filterJobType DtleJobType) error { logger := handler.NewLogger().Named("PauseJobV2") reqParam := new(models.PauseJobReqV2) if err := handler.BindAndValidate(logger, c, reqParam); err != nil { return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } - + if filterJobType != GetJobTypeFromJobId(reqParam.JobId) { + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(fmt.Errorf("only supports job of type %v", filterJobType))) + } err := checkJobAccess(c, reqParam.JobId) if err != nil { return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) @@ -1220,21 +1279,51 @@ func PauseJobV2(c echo.Context) error { }) } -// @Id ResumeJobV2 -// @Description resume job. +// @Id ResumeMigrationJobV2 +// @Description resume migration job. +// @Tags job +// @accept application/x-www-form-urlencoded +// @Security ApiKeyAuth +// @Param job_id formData string true "job id" +// @Success 200 {object} models.ResumeJobRespV2 +// @Router /v2/job/migration/resume [post] +func ResumeMigrationJobV2(c echo.Context) error { + return ResumeJobV2(c, DtleJobTypeMigration) +} + +// @Id ResumeSyncJobV2 +// @Description resume sync job. +// @Tags job +// @accept application/x-www-form-urlencoded +// @Security ApiKeyAuth +// @Param job_id formData string true "job id" +// @Success 200 {object} models.ResumeJobRespV2 +// @Router /v2/job/sync/resume [post] +func ResumeSyncJobV2(c echo.Context) error { + return ResumeJobV2(c, DtleJobTypeSync) +} + +// @Id ResumeSubscriptionJobV2 +// @Description resume subscription job. // @Tags job // @accept application/x-www-form-urlencoded // @Security ApiKeyAuth // @Param job_id formData string true "job id" // @Success 200 {object} models.ResumeJobRespV2 -// @Router /v2/job/resume [post] -func ResumeJobV2(c echo.Context) error { +// @Router /v2/job/subscription/resume [post] +func ResumeSubscriptionJobV2(c echo.Context) error { + return ResumeJobV2(c, DtleJobTypeSubscription) +} + +func ResumeJobV2(c echo.Context, filterJobType DtleJobType) error { logger := handler.NewLogger().Named("ResumeJobV2") reqParam := new(models.ResumeJobReqV2) if err := handler.BindAndValidate(logger, c, reqParam); err != nil { return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } - + if filterJobType != GetJobTypeFromJobId(reqParam.JobId) { + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(fmt.Errorf("only supports job of type %v", filterJobType))) + } err := checkJobAccess(c, reqParam.JobId) if err != nil { return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) @@ -1316,20 +1405,51 @@ func sentSignalToTask(logger hclog.Logger, allocId, signal string) error { return nil } -// @Id DeleteJobV2 -// @Description delete job. +// @Id DeleteMigrationJobV2 +// @Description delete migration job. // @Tags job // @accept application/x-www-form-urlencoded // @Security ApiKeyAuth // @Param job_id formData string true "job id" // @Success 200 {object} models.DeleteJobRespV2 -// @Router /v2/job/delete [post] -func DeleteJobV2(c echo.Context) error { +// @Router /v2/job/migration/delete [post] +func DeleteMigrationJobV2(c echo.Context) error { + return DeleteJobV2(c, DtleJobTypeMigration) +} + +// @Id DeleteSyncJobV2 +// @Description delete sync job. +// @Tags job +// @accept application/x-www-form-urlencoded +// @Security ApiKeyAuth +// @Param job_id formData string true "job id" +// @Success 200 {object} models.DeleteJobRespV2 +// @Router /v2/job/sync/delete [post] +func DeleteSyncJobV2(c echo.Context) error { + return DeleteJobV2(c, DtleJobTypeSync) +} + +// @Id DeleteSubscriptionJobV2 +// @Description delete subscription job. +// @Tags job +// @accept application/x-www-form-urlencoded +// @Security ApiKeyAuth +// @Param job_id formData string true "job id" +// @Success 200 {object} models.DeleteJobRespV2 +// @Router /v2/job/subscription/delete [post] +func DeleteSubscriptionJobV2(c echo.Context) error { + return DeleteJobV2(c, DtleJobTypeSubscription) +} + +func DeleteJobV2(c echo.Context, filterJobType DtleJobType) error { logger := handler.NewLogger().Named("DeleteJobV2") reqParam := new(models.DeleteJobReqV2) if err := handler.BindAndValidate(logger, c, reqParam); err != nil { return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } + if filterJobType != GetJobTypeFromJobId(reqParam.JobId) { + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(fmt.Errorf("only supports job of type %v", filterJobType))) + } err := checkJobAccess(c, reqParam.JobId) if err != nil { return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) @@ -1393,6 +1513,32 @@ func GetJobGtidV2(c echo.Context) error { }) } +// @Summary start reverse-init job +// @Id ReverseStartMigrationJobV2 +// @Tags job +// @Description Start Reverse Job. +// @accept application/x-www-form-urlencoded +// @Security ApiKeyAuth +// @Param job_id formData string true "job id" +// @Success 200 {object} models.ReverseStartRespV2 +// @Router /v2/job/migration/reverse_start [post] +func ReverseStartMigrationJobV2(c echo.Context) error { + return ReverseStartJobV2(c, DtleJobTypeMigration) +} + +// @Summary start reverse-init job +// @Id ReverseStartSyncJobV2 +// @Tags job +// @Description Start Reverse Job. +// @accept application/x-www-form-urlencoded +// @Security ApiKeyAuth +// @Param job_id formData string true "job id" +// @Success 200 {object} models.ReverseStartRespV2 +// @Router /v2/job/sync/reverse_start [post] +func ReverseStartSyncJobV2(c echo.Context) error { + return ReverseStartJobV2(c, DtleJobTypeSync) +} + // @Summary start reverse-init job // @Id ReverseStartJobV2 // @Tags job @@ -1402,12 +1548,15 @@ func GetJobGtidV2(c echo.Context) error { // @Param job_id formData string true "job id" // @Success 200 {object} models.ReverseStartRespV2 // @Router /v2/job/reverse_start [post] -func ReverseStartJobV2(c echo.Context) error { +func ReverseStartJobV2(c echo.Context, filterJobType DtleJobType) error { logger := handler.NewLogger().Named("ReverseStartJobV2") reqParam := new(models.ReverseStartReqV2) if err := handler.BindAndValidate(logger, c, reqParam); err != nil { return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } + if filterJobType != GetJobTypeFromJobId(reqParam.JobId) { + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(fmt.Errorf("only supports job of type %v", filterJobType))) + } err := checkJobAccess(c, reqParam.JobId) if err != nil { return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) @@ -1471,21 +1620,40 @@ func ReverseStartJobV2(c echo.Context) error { }) } -// @Id ReverseJobV2 -// @Description returnJob +// @Id ReverseMigrationJobV2 +// @Description reverse migration Job +// @Tags job +// @Accept application/json +// @Security ApiKeyAuth +// @Param reverse_config body models.ReverseJobReq true "reverse config config" +// @Success 200 {object} models.ReverseJobResp +// @Router /v2/job/migration/reverse [post] +func ReverseMigrationJobV2(c echo.Context) error { + return ReverseJobV2(c, DtleJobTypeMigration) +} + +// @Id ReverseSyncJobV2 +// @Description reverse sync Job // @Tags job // @Accept application/json // @Security ApiKeyAuth // @Param reverse_config body models.ReverseJobReq true "reverse config config" // @Success 200 {object} models.ReverseJobResp -// @Router /v2/job/reverse [post] -func ReverseJobV2(c echo.Context) error { +// @Router /v2/job/sync/reverse [post] +func ReverseSyncJobV2(c echo.Context) error { + return ReverseJobV2(c, DtleJobTypeSync) +} + +func ReverseJobV2(c echo.Context, filterJobType DtleJobType) error { logger := handler.NewLogger().Named("ReverseJobV2") reqParam := new(models.ReverseJobReq) if err := handler.BindAndValidate(logger, c, reqParam); err != nil { return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } + if GetJobTypeFromJobId(reqParam.JobId) != filterJobType { + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(fmt.Errorf("cannot operate job of type %v", filterJobType))) + } err := checkJobAccess(c, reqParam.JobId) if err != nil { return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) diff --git a/drivers/api/handler/v2/login.go b/drivers/api/handler/v2/login.go index 353c6123c..0928efdd0 100644 --- a/drivers/api/handler/v2/login.go +++ b/drivers/api/handler/v2/login.go @@ -33,7 +33,12 @@ func LoginV2(c echo.Context) error { if err := handler.BindAndValidate(logger, c, reqParam); err != nil { return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } + blackListKey := fmt.Sprintf("%s:%s:%s", reqParam.Tenant, reqParam.Username, "login") + if leftMinute, exist := BL.blackListExist(blackListKey); exist { + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(fmt.Errorf("the password cannot be changed temporarily, please try again after %v minute", leftMinute))) + } if !store.Verify(reqParam.CaptchaId, reqParam.Captcha, true) { + BL.setBlackList(fmt.Sprintf("%s:%s:%s", reqParam.Tenant, reqParam.Username, "login"), time.Minute*30) return c.JSON(http.StatusBadRequest, models.BuildBaseResp(fmt.Errorf("verfied failed"))) } storeManager, err := common.NewStoreManager([]string{handler.ConsulAddr}, logger) @@ -48,7 +53,7 @@ func LoginV2(c echo.Context) error { return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(fmt.Errorf("user or password is wrong"))) } - if err := ValidatePassword(fmt.Sprintf("%s:%s", reqParam.Tenant, reqParam.Username), "login", reqParam.Password, user.Password); err != nil { + if err := ValidatePassword(blackListKey, reqParam.Password, user.Password); err != nil { return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } diff --git a/drivers/api/handler/v2/user.go b/drivers/api/handler/v2/user.go index fbbcdffca..2085ab945 100644 --- a/drivers/api/handler/v2/user.go +++ b/drivers/api/handler/v2/user.go @@ -236,7 +236,7 @@ func ResetPasswordV2(c echo.Context) error { if !exist { return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(fmt.Errorf("user does not exist"))) } - if err := ValidatePassword(fmt.Sprintf("%s:%s", reqParam.Tenant, reqParam.Username), "reset_pwd", user.Password, reqParam.OldPassWord); err != nil { + if err := ValidatePassword(fmt.Sprintf("%s:%s:%s", reqParam.Tenant, reqParam.Username, "reset_pwd"), user.Password, reqParam.OldPassWord); err != nil { return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } diff --git a/drivers/api/route.go b/drivers/api/route.go index 531de33e7..609b77c34 100644 --- a/drivers/api/route.go +++ b/drivers/api/route.go @@ -3,7 +3,6 @@ package api import ( "encoding/json" "fmt" - "io/ioutil" "net/http" "path" "strings" @@ -85,27 +84,40 @@ func SetupApiServer(logger hclog.Logger, driverConfig *mysql.DriverConfig) (err e.POST("/v2/login/captcha", v2.CaptchaV2) e.GET("/v2/monitor/task", v2.GetTaskProgressV2) v2Router.POST("/log/level", v2.UpdateLogLevelV2) - v2Router.GET("/jobs", v2.JobListV2) + v2Router.GET("/jobs/migration", v2.MigrationJobListV2) v2Router.GET("/job/migration/detail", v2.GetMigrationJobDetailV2) v2Router.POST("/job/migration/create", v2.CreateMigrationJobV2) v2Router.POST("/job/migration/update", v2.UpdateMigrationJobV2) + v2Router.POST("/job/migration/reverse", v2.ReverseMigrationJobV2) + v2Router.POST("/job/migration/pause", v2.PauseMigrationJobV2) + v2Router.POST("/job/migration/resume", v2.ResumeMigrationJobV2) + v2Router.POST("/job/migration/delete", v2.DeleteMigrationJobV2) + v2Router.POST("/job/migration/reverse_start", v2.ReverseStartMigrationJobV2) + + v2Router.GET("/jobs/sync", v2.SyncJobListV2) v2Router.GET("/job/sync/detail", v2.GetSyncJobDetailV2) v2Router.POST("/job/sync/create", v2.CreateSyncJobV2) v2Router.POST("/job/sync/update", v2.UpdateSyncJobV2) + v2Router.POST("/job/sync/reverse", v2.ReverseSyncJobV2) + v2Router.POST("/job/sync/pause", v2.PauseSyncJobV2) + v2Router.POST("/job/sync/resume", v2.ResumeSyncJobV2) + v2Router.POST("/job/sync/delete", v2.DeleteSyncJobV2) + v2Router.POST("/job/sync/reverse_start", v2.ReverseStartSyncJobV2) + + v2Router.GET("/jobs/subscription", v2.SubscriptionJobListV2) v2Router.GET("/job/subscription/detail", v2.GetSubscriptionJobDetailV2) v2Router.POST("/job/subscription/create", v2.CreateSubscriptionJobV2) v2Router.POST("/job/subscription/update", v2.UpdateSubscriptionJobV2) - v2Router.POST("/job/pause", v2.PauseJobV2) - v2Router.POST("/job/resume", v2.ResumeJobV2) - v2Router.POST("/job/delete", v2.DeleteJobV2) + v2Router.POST("/job/subscription/pause", v2.PauseSubscriptionJobV2) + v2Router.POST("/job/subscription/resume", v2.ResumeSubscriptionJobV2) + v2Router.POST("/job/subscription/delete", v2.DeleteSubscriptionJobV2) + v2Router.GET("/nodes", v2.NodeListV2) v2Router.POST("/validation/job", v2.ValidateJobV2) v2Router.GET("/mysql/schemas", v2.ListMysqlSchemasV2) v2Router.GET("/mysql/columns", v2.ListMysqlColumnsV2) v2Router.GET("/mysql/instance_connection", v2.ConnectionV2) v2Router.GET("/job/gtid", v2.GetJobGtidV2) - v2Router.POST("/job/reverse_start", v2.ReverseStartJobV2) - v2Router.POST("/job/reverse", v2.ReverseJobV2) v2Router.GET("/user/list", v2.UserListV2) v2Router.POST("/user/create", v2.CreateUserV2) v2Router.POST("/user/update", v2.UpdateUserV2) @@ -212,14 +224,11 @@ func AuthFilter() echo.MiddlewareFunc { authority := make([]models.MenuItem, 0) err = json.Unmarshal([]byte(role.Authority), &authority) if err != nil { - return echo.NewHTTPError(http.StatusForbidden, "please check your authority") + return echo.NewHTTPError(http.StatusForbidden, fmt.Sprintf("check your authority fail : %v", err)) } for _, menuItem := range authority { for _, buttonItem := range menuItem.Operations { if c.Request().URL.Path == buttonItem.Uri { - if !checkForJob(c, menuItem.Name, buttonItem.Uri) { - continue - } return next(c) } } @@ -229,66 +238,6 @@ func AuthFilter() echo.MiddlewareFunc { } } -func checkForJob(c echo.Context, menuType, uri string) bool { - logger := handler.NewLogger().Named("checkForJob") - logger.Error("before", "uri", uri, "menuType", menuType) - var jobType v2.DtleJobType - switch uri { - case "/v2/jobs": - reqParam := new(models.JobListReqV2) - if err := c.Bind(reqParam); nil != err { - return false - } - jobType = v2.DtleJobType(reqParam.FilterJobType) - case "/v2/job/pause": - reqParam := new(models.PauseJobReqV2) - if err := c.Bind(reqParam); nil != err { - return false - } - jobType = v2.GetJobTypeFromJobId(reqParam.JobId) - case "/v2/job/resume": - reqParam := new(models.ResumeJobReqV2) - if err := c.Bind(reqParam); nil != err { - return false - } - jobType = v2.GetJobTypeFromJobId(reqParam.JobId) - case "/v2/job/delete": - reqParam := new(models.DeleteJobReqV2) - if err := c.Bind(reqParam); nil != err { - return false - } - jobType = v2.GetJobTypeFromJobId(reqParam.JobId) - case "/v2/job/reverse": - body, err := c.Request().GetBody() - if err != nil { - return false - } - bytes, err := ioutil.ReadAll(body) - if err != nil { - return false - } - reqParam := new(models.ReverseJobReq) - err = json.Unmarshal(bytes, reqParam) - if err != nil { - return false - } - jobType = v2.GetJobTypeFromJobId(reqParam.JobId) - case "/v2/job/reverse_start": - reqParam := new(models.ReverseStartReqV2) - if err := c.Bind(reqParam); nil != err { - return false - } - jobType = v2.GetJobTypeFromJobId(reqParam.JobId) - default: - return true - } - logger.Error("after", "jobType", jobType, "menuType", menuType) - if string(jobType) == menuType { - return true - } - return false -} - var whiteList = []string{ "/v2/nodes", "/v2/validation/job", diff --git a/drivers/mysql/common/common.go b/drivers/mysql/common/common.go index 93713af85..9140cf4f7 100644 --- a/drivers/mysql/common/common.go +++ b/drivers/mysql/common/common.go @@ -32,14 +32,14 @@ const ( ) const ( + // TODO: Using configuration to set jwt secret + JWTSecret = "secret" DefaultAdminTenant = "platform" DefaultAdminUser = "admin" DefaultAdminPwd = "admin" DefaultEncryptAdminPwd = "rAMd28u1e8j2cGfXeuef6ChGe8+SFP0b29KuJ0w9hvo2B2HbVTYaAp2E+vdBBE3KpKfo1HfOOezy8WFdk40v/xjM6Gwfhmji2czpKdfaGdnbkvChfqv2taPo8WFeRaGiaIEZ1Ygu0eKz1Yq+FOIEwNjH+clthxPqX3hiizSBbHA=" DefaultRole = "admin" - DefaultAdminAuth = "[{\"name\":\"service\",\"text_cn\":\"服务\",\"text_en\":\"service\",\"menu_level\":1,\"menu_url\":\"\",\"id\":1,\"parent_id\":0,\"operations\":[]},{\"name\":\"migration\",\"text_cn\":\"数据迁移\",\"text_en\":\"migration\",\"menu_level\":2,\"menu_url\":\"/migration\",\"id\":2,\"parent_id\":1,\"operations\":[{\"action\":\"migration-list\",\"uri\":\"/v2/jobs\",\"text_cn\":\"任务列表\",\"text_en\":\"job list\"},{\"action\":\"migration-create\",\"uri\":\"/v2/job/migration/create\",\"text_cn\":\"创建任务\",\"text_en\":\"create\"},{\"action\":\"migration-pause\",\"uri\":\"/v2/job/pause\",\"text_cn\":\"暂停任务\",\"text_en\":\"pause\"},{\"action\":\"migration-resume\",\"uri\":\"/v2/job/resume\",\"text_cn\":\"重启任务\",\"text_en\":\"resume\"},{\"action\":\"migration-delete\",\"uri\":\"/v2/job/delete\",\"text_cn\":\"销毁任务\",\"text_en\":\"delete\"},{\"action\":\"migration-reverse\",\"uri\":\"/v2/job/reverse\",\"text_cn\":\"创建反向复制任务\",\"text_en\":\"reverse\"},{\"action\":\"migration-reverse_start\",\"uri\":\"/v2/job/reverse_start\",\"text_cn\":\"启动反向任务\",\"text_en\":\"start reverse\"},{\"action\":\"migration-update\",\"uri\":\"/v2/job/migration/update\",\"text_cn\":\"修改任务\",\"text_en\":\"update\"},{\"action\":\"migration-detail\",\"uri\":\"/v2/job/migration/detail\",\"text_cn\":\"查看详情\",\"text_en\":\"detail\"}]},{\"name\":\"sync\",\"text_cn\":\"数据同步\",\"text_en\":\"sync\",\"menu_level\":2,\"menu_url\":\"/sync\",\"id\":3,\"parent_id\":1,\"operations\":[{\"action\":\"sync-list\",\"uri\":\"/v2/jobs\",\"text_cn\":\"任务列表\",\"text_en\":\"job list\"},{\"action\":\"sync-create\",\"uri\":\"/v2/job/sync/create\",\"text_cn\":\"创建任务\",\"text_en\":\"create\"},{\"action\":\"sync-pause\",\"uri\":\"/v2/job/pause\",\"text_cn\":\"暂停任务\",\"text_en\":\"pause\"},{\"action\":\"sync-resume\",\"uri\":\"/v2/job/resume\",\"text_cn\":\"重启任务\",\"text_en\":\"resume\"},{\"action\":\"sync-delete\",\"uri\":\"/v2/job/delete\",\"text_cn\":\"销毁任务\",\"text_en\":\"delete\"},{\"action\":\"sync-reverse\",\"uri\":\"/v2/job/reverse\",\"text_cn\":\"反向复制\",\"text_en\":\"reverse\"},{\"action\":\"sync-reverse_start\",\"uri\":\"/v2/job/reverse_start\",\"text_cn\":\"启动反向任务\",\"text_en\":\"reverse start\"},{\"action\":\"sync-update\",\"uri\":\"/v2/job/sync/update\",\"text_cn\":\"修改任务\",\"text_en\":\"update\"},{\"action\":\"sync-detail\",\"uri\":\"/v2/job/sync/detail\",\"text_cn\":\"查看详情\",\"text_en\":\"detail\"}]},{\"name\":\"subscription\",\"text_cn\":\"数据订阅\",\"text_en\":\"subscription\",\"menu_level\":2,\"menu_url\":\"/subscribe\",\"id\":4,\"parent_id\":1,\"operations\":[{\"action\":\"subscription-list\",\"uri\":\"/v2/jobs\",\"text_cn\":\"任务列表\",\"text_en\":\"job list\"},{\"action\":\"subscription-create\",\"uri\":\"/v2/job/subscription/create\",\"text_cn\":\"创建任务\",\"text_en\":\"create\"},{\"action\":\"subscription-pause\",\"uri\":\"/v2/job/pause\",\"text_cn\":\"暂停任务\",\"text_en\":\"pause\"},{\"action\":\"subscription-resume\",\"uri\":\"/v2/job/resume\",\"text_cn\":\"重启任务\",\"text_en\":\"resume\"},{\"action\":\"subscription-delete\",\"uri\":\"/v2/job/delete\",\"text_cn\":\"销毁任务\",\"text_en\":\"delete\"},{\"action\":\"subscription-update\",\"uri\":\"/v2/job/subscription/update\",\"text_cn\":\"修改任务\",\"text_en\":\"update\"},{\"action\":\"subscription-detail\",\"uri\":\"/v2/job/subscription/detail\",\"text_cn\":\"查看详情\",\"text_en\":\"detail\"}]},{\"name\":\"platform\",\"text_cn\":\"平台管理\",\"text_en\":\"platform\",\"menu_level\":1,\"menu_url\":\"\",\"id\":5,\"parent_id\":0,\"operations\":[]},{\"name\":\"node\",\"text_cn\":\"DTLE节点\",\"text_en\":\"dtle nodes\",\"menu_level\":2,\"menu_url\":\"/node\",\"id\":7,\"parent_id\":5,\"operations\":[{\"action\":\"node-list\",\"uri\":\"/v2/nodes\",\"text_cn\":\"获取节点列表\",\"text_en\":\"get node list\"}]},{\"name\":\"users\",\"admin_only\":true,\"text_cn\":\"用户管理\",\"text_en\":\"user manage\",\"menu_level\":2,\"menu_url\":\"/users\",\"id\":6,\"parent_id\":5,\"operations\":[{\"action\":\"user-list\",\"uri\":\"/v2/user/list\",\"text_cn\":\"查看用户列表\",\"text_en\":\"user list\"},{\"action\":\"user-list_tenant\",\"uri\":\"/v2/tenant/list\",\"text_cn\":\"获取租户列表\",\"text_en\":\"get tenants\"},{\"action\":\"user-create\",\"uri\":\"/v2/user/create\",\"text_cn\":\"创建用户\",\"text_en\":\"create user\"},{\"action\":\"user-delete\",\"uri\":\"/v2/user/delete\",\"text_cn\":\"删除用户\",\"text_en\":\"delete\"},{\"action\":\"user-update\",\"uri\":\"/v2/user/update\",\"text_cn\":\"修改用户\",\"text_en\":\"update user\"}]},{\"name\":\"auth\",\"admin_only\":true,\"text_cn\":\"权限配置\",\"text_en\":\"rights profile\",\"menu_level\":2,\"menu_url\":\"/auth\",\"id\":8,\"parent_id\":5,\"operations\":[{\"action\":\"auth-list\",\"uri\":\"/v2/role/list\",\"text_cn\":\"查看角色列表\",\"text_en\":\"role list\"},{\"action\":\"auth-create\",\"uri\":\"/v2/role/create\",\"text_cn\":\"创建角色\",\"text_en\":\"create\"},{\"action\":\"auth-delete\",\"uri\":\"/v2/role/delete\",\"text_cn\":\"删除角色\",\"text_en\":\"delete\"},{\"action\":\"auth-update\",\"uri\":\"/v2/role/update\",\"text_cn\":\"修改角色\",\"text_en\":\"update\"}]}]" - // TODO: Using configuration to set jwt secret - JWTSecret = "secret" + DefaultAdminAuth = "[{\"name\":\"service\",\"text_cn\":\"服务\",\"text_en\":\"service\",\"menu_level\":1,\"menu_url\":\"\",\"id\":1,\"parent_id\":0,\"operations\":[]},{\"name\":\"migration\",\"text_cn\":\"数据迁移\",\"text_en\":\"migration\",\"menu_level\":2,\"menu_url\":\"/migration\",\"id\":2,\"parent_id\":1,\"operations\":[{\"action\":\"migration-list\",\"uri\":\"/v2/jobs/migration\",\"text_cn\":\"迁移任务列表\",\"text_en\":\"migration job list\"},{\"action\":\"migration-create\",\"uri\":\"/v2/job/migration/create\",\"text_cn\":\"迁移创建任务\",\"text_en\":\"create migration job\"},{\"action\":\"migration-pause\",\"uri\":\"/v2/job/migration/pause\",\"text_cn\":\"暂停任务\",\"text_en\":\"pause migration job\"},{\"action\":\"migration-resume\",\"uri\":\"/v2/job/migration/resume\",\"text_cn\":\"重启迁移任务\",\"text_en\":\"resume migration job\"},{\"action\":\"migration-delete\",\"uri\":\"/v2/job/migration/delete\",\"text_cn\":\"销毁迁移任务\",\"text_en\":\"delete migration job\"},{\"action\":\"migration-reverse\",\"uri\":\"/v2/job/migration/reverse\",\"text_cn\":\"创建迁移反向复制任务\",\"text_en\":\"reverse migration job\"},{\"action\":\"migration-reverse_start\",\"uri\":\"/v2/job/migration/reverse_start\",\"text_cn\":\"启动迁移反向任务\",\"text_en\":\"start migration reverse job \"},{\"action\":\"migration-update\",\"uri\":\"/v2/job/migration/update\",\"text_cn\":\"修改迁移任务\",\"text_en\":\"update migration job\"},{\"action\":\"migration-detail\",\"uri\":\"/v2/job/migration/detail\",\"text_cn\":\"查看迁移任务详情\",\"text_en\":\"migration detail job\"}]},{\"name\":\"sync\",\"text_cn\":\"数据同步\",\"text_en\":\"sync\",\"menu_level\":2,\"menu_url\":\"/sync\",\"id\":3,\"parent_id\":1,\"operations\":[{\"action\":\"sync-list\",\"uri\":\"/v2/jobs/sync\",\"text_cn\":\"同步任务列表\",\"text_en\":\"sync job list\"},{\"action\":\"sync-create\",\"uri\":\"/v2/job/sync/create\",\"text_cn\":\"创建同步任务\",\"text_en\":\"create sync job\"},{\"action\":\"sync-pause\",\"uri\":\"/v2/job/sync/pause\",\"text_cn\":\"暂停同步任务\",\"text_en\":\"pause sync job\"},{\"action\":\"sync-resume\",\"uri\":\"/v2/job/sync/resume\",\"text_cn\":\"重启同步任务\",\"text_en\":\"resume sync job\"},{\"action\":\"sync-delete\",\"uri\":\"/v2/job/sync/delete\",\"text_cn\":\"销毁同步任务\",\"text_en\":\"delete sync job\"},{\"action\":\"sync-reverse\",\"uri\":\"/v2/job/sync/reverse\",\"text_cn\":\"反向复制同步任务\",\"text_en\":\"reverse sync job\"},{\"action\":\"sync-reverse_start\",\"uri\":\"/v2/job/sync/reverse_start\",\"text_cn\":\"启动同步反向任务\",\"text_en\":\"reverse start sync job\"},{\"action\":\"sync-update\",\"uri\":\"/v2/job/sync/update\",\"text_cn\":\"修改同步任务\",\"text_en\":\"update sync job\"},{\"action\":\"sync-detail\",\"uri\":\"/v2/job/sync/detail\",\"text_cn\":\"查看同步任务详情\",\"text_en\":\"sync job detail \"}]},{\"name\":\"subscription\",\"text_cn\":\"数据订阅\",\"text_en\":\"subscription\",\"menu_level\":2,\"menu_url\":\"/subscribe\",\"id\":4,\"parent_id\":1,\"operations\":[{\"action\":\"subscription-list\",\"uri\":\"/v2/jobs/subscription\",\"text_cn\":\"订阅任务列表\",\"text_en\":\"subscription job list\"},{\"action\":\"subscription-create\",\"uri\":\"/v2/job/subscription/create\",\"text_cn\":\"创建订阅任务\",\"text_en\":\"create subscription job\"},{\"action\":\"subscription-pause\",\"uri\":\"/v2/job/subscription/pause\",\"text_cn\":\"暂停订阅任务\",\"text_en\":\"pause subscription job\"},{\"action\":\"subscription-resume\",\"uri\":\"/v2/job/subscription/resume\",\"text_cn\":\"重启订阅任务\",\"text_en\":\"resume subscription job\"},{\"action\":\"subscription-delete\",\"uri\":\"/v2/job/subscription/delete\",\"text_cn\":\"销毁订阅任务\",\"text_en\":\"delete subscription job\"},{\"action\":\"subscription-update\",\"uri\":\"/v2/job/subscription/update\",\"text_cn\":\"修改订阅任务\",\"text_en\":\"update subscription job\"},{\"action\":\"subscription-detail\",\"uri\":\"/v2/job/subscription/detail\",\"text_cn\":\"查看订阅任务详情\",\"text_en\":\"subscription job detail\"}]},{\"name\":\"platform\",\"text_cn\":\"平台管理\",\"text_en\":\"platform\",\"menu_level\":1,\"menu_url\":\"\",\"id\":5,\"parent_id\":0,\"operations\":[]},{\"name\":\"node\",\"text_cn\":\"DTLE节点\",\"text_en\":\"dtle nodes\",\"menu_level\":2,\"menu_url\":\"/node\",\"id\":7,\"parent_id\":5,\"operations\":[{\"action\":\"node-list\",\"uri\":\"/v2/nodes\",\"text_cn\":\"获取节点列表\",\"text_en\":\"get node list\"}]},{\"name\":\"users\",\"admin_only\":true,\"text_cn\":\"用户管理\",\"text_en\":\"user manage\",\"menu_level\":2,\"menu_url\":\"/users\",\"id\":6,\"parent_id\":5,\"operations\":[{\"action\":\"user-list\",\"uri\":\"/v2/user/list\",\"text_cn\":\"查看用户列表\",\"text_en\":\"user list\"},{\"action\":\"user-list_tenant\",\"uri\":\"/v2/tenant/list\",\"text_cn\":\"获取租户列表\",\"text_en\":\"get tenants\"},{\"action\":\"user-create\",\"uri\":\"/v2/user/create\",\"text_cn\":\"创建用户\",\"text_en\":\"create user\"},{\"action\":\"user-delete\",\"uri\":\"/v2/user/delete\",\"text_cn\":\"删除用户\",\"text_en\":\"delete\"},{\"action\":\"user-update\",\"uri\":\"/v2/user/update\",\"text_cn\":\"修改用户\",\"text_en\":\"update user\"}]},{\"name\":\"auth\",\"admin_only\":true,\"text_cn\":\"权限配置\",\"text_en\":\"rights profile\",\"menu_level\":2,\"menu_url\":\"/auth\",\"id\":8,\"parent_id\":5,\"operations\":[{\"action\":\"auth-list\",\"uri\":\"/v2/role/list\",\"text_cn\":\"查看角色列表\",\"text_en\":\"role list\"},{\"action\":\"auth-create\",\"uri\":\"/v2/role/create\",\"text_cn\":\"创建角色\",\"text_en\":\"create\"},{\"action\":\"auth-delete\",\"uri\":\"/v2/role/delete\",\"text_cn\":\"删除角色\",\"text_en\":\"delete\"},{\"action\":\"auth-update\",\"uri\":\"/v2/role/update\",\"text_cn\":\"修改角色\",\"text_en\":\"update\"}]}]" ) const ( From 8f8cd681b88ccba32d75f374fbb9567aa8882e91 Mon Sep 17 00:00:00 2001 From: yangzhongjiao <451773851@qq.com> Date: Mon, 23 Aug 2021 13:44:39 +0800 Subject: [PATCH 4/5] modify login/reset_pwd err message --- drivers/api/handler/v2/login.go | 2 +- drivers/api/handler/v2/user.go | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/api/handler/v2/login.go b/drivers/api/handler/v2/login.go index 0928efdd0..4308463b7 100644 --- a/drivers/api/handler/v2/login.go +++ b/drivers/api/handler/v2/login.go @@ -35,7 +35,7 @@ func LoginV2(c echo.Context) error { } blackListKey := fmt.Sprintf("%s:%s:%s", reqParam.Tenant, reqParam.Username, "login") if leftMinute, exist := BL.blackListExist(blackListKey); exist { - return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(fmt.Errorf("the password cannot be changed temporarily, please try again after %v minute", leftMinute))) + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(fmt.Errorf("you cannot be login temporarily, please try again after %v minute", leftMinute))) } if !store.Verify(reqParam.CaptchaId, reqParam.Captcha, true) { BL.setBlackList(fmt.Sprintf("%s:%s:%s", reqParam.Tenant, reqParam.Username, "login"), time.Minute*30) diff --git a/drivers/api/handler/v2/user.go b/drivers/api/handler/v2/user.go index 2085ab945..acc8191ae 100644 --- a/drivers/api/handler/v2/user.go +++ b/drivers/api/handler/v2/user.go @@ -219,6 +219,10 @@ func ResetPasswordV2(c echo.Context) error { if err := handler.BindAndValidate(logger, c, reqParam); err != nil { return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } + blackListKey := fmt.Sprintf("%s:%s:%s", reqParam.Tenant, reqParam.Username, "reset_pwd") + if leftMinute, exist := BL.blackListExist(blackListKey); exist { + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(fmt.Errorf("you cannot be login temporarily, please try again after %v minute", leftMinute))) + } if hasAccess, err := checkUserAccess(logger, c, fmt.Sprintf("%s:%s", reqParam.Tenant, reqParam.Username)); err != nil || !hasAccess { return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(fmt.Errorf("current user has no access to operate group %v ; err : %v", reqParam.Tenant, err))) @@ -236,7 +240,7 @@ func ResetPasswordV2(c echo.Context) error { if !exist { return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(fmt.Errorf("user does not exist"))) } - if err := ValidatePassword(fmt.Sprintf("%s:%s:%s", reqParam.Tenant, reqParam.Username, "reset_pwd"), user.Password, reqParam.OldPassWord); err != nil { + if err := ValidatePassword(blackListKey, user.Password, reqParam.OldPassWord); err != nil { return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } From ac86858a8726ae3a468a33851e9cb1c141071ab2 Mon Sep 17 00:00:00 2001 From: yangzhongjiao <451773851@qq.com> Date: Mon, 23 Aug 2021 14:48:12 +0800 Subject: [PATCH 5/5] fix create/update job fail --- drivers/api/handler/v2/job.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/api/handler/v2/job.go b/drivers/api/handler/v2/job.go index 2a5448084..faa495493 100644 --- a/drivers/api/handler/v2/job.go +++ b/drivers/api/handler/v2/job.go @@ -1788,7 +1788,7 @@ func checkUpdateJobInfo(c echo.Context, jobId string, create bool) error { if err != nil { return fmt.Errorf("consul_addr=%v; connect to consul failed: %v", handler.ConsulAddr, err) } - if storeManager.CheckJobExists(jobId) != create { + if storeManager.CheckJobExists(jobId) == create { return fmt.Errorf("please confirm whether the job [ %v ] already exists", jobId) } jobInfo, err := storeManager.GetJobInfo(jobId)