diff --git a/drivers/api/docs/docs.go b/drivers/api/docs/docs.go index 147db740d..11c3317eb 100644 --- a/drivers/api/docs/docs.go +++ b/drivers/api/docs/docs.go @@ -24,21 +24,435 @@ var doc = `{ "host": "{{.Host}}", "basePath": "{{.BasePath}}", "paths": { - "/v2/job/delete": { + "/v2/job/gtid": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "get src task current gtid.", + "tags": [ + "job" + ], + "operationId": "GetJobGtidV2", + "parameters": [ + { + "type": "string", + "description": "job id", + "name": "job_id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.JobGtidResp" + } + } + } + } + }, + "/v2/job/migration/create": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "create migration job.", + "consumes": [ + "application/json" + ], + "tags": [ + "job" + ], + "operationId": "CreateMigrationJobV2", + "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/migration/delete": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "delete migration job.", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "tags": [ + "job" + ], + "operationId": "DeleteMigrationJobV2", + "parameters": [ + { + "type": "string", + "description": "job id", + "name": "job_id", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.DeleteJobRespV2" + } + } + } + } + }, + "/v2/job/migration/detail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "get migration job detail.", + "tags": [ + "job" + ], + "operationId": "GetMigrationJobDetailV2", + "parameters": [ + { + "type": "string", + "description": "job id", + "name": "job_id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.MysqlToMysqlJobDetailRespV2" + } + } + } + } + }, + "/v2/job/migration/pause": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "pause migration job.", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "tags": [ + "job" + ], + "operationId": "PauseMigrationJobV2", + "parameters": [ + { + "type": "string", + "description": "job id", + "name": "job_id", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.PauseJobRespV2" + } + } + } + } + }, + "/v2/job/migration/resume": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "resume migration job.", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "tags": [ + "job" + ], + "operationId": "ResumeMigrationJobV2", + "parameters": [ + { + "type": "string", + "description": "job id", + "name": "job_id", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.ResumeJobRespV2" + } + } + } + } + }, + "/v2/job/migration/reverse": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "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" + ], + "summary": "start reverse-init job", + "operationId": "ReverseStartMigrationJobV2", + "parameters": [ + { + "type": "string", + "description": "job id", + "name": "job_id", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.ReverseStartRespV2" + } + } + } + } + }, + "/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/reverse_start": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Finish Job.", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "tags": [ + "job" + ], + "summary": "start reverse-init job", + "operationId": "ReverseStartJobV2", + "parameters": [ + { + "type": "string", + "description": "job id", + "name": "job_id", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.ReverseStartRespV2" + } + } + } + } + }, + "/v2/job/subscription": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "create or update subscription job.", + "consumes": [ + "application/json" + ], + "tags": [ + "job" + ], + "operationId": "CreateOrUpdateSubscriptionJobV2", + "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/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/delete": { "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "delete job.", + "description": "delete subscription job.", "consumes": [ "application/x-www-form-urlencoded" ], "tags": [ "job" ], - "operationId": "DeleteJobV2", + "operationId": "DeleteSubscriptionJobV2", "parameters": [ { "type": "string", @@ -58,18 +472,18 @@ var doc = `{ } } }, - "/v2/job/gtid": { + "/v2/job/subscription/detail": { "get": { "security": [ { "ApiKeyAuth": [] } ], - "description": "get src task current gtid.", + "description": "get subscription job detail.", "tags": [ "job" ], - "operationId": "GetJobGtidV2", + "operationId": "GetSubscriptionJobDetailV2", "parameters": [ { "type": "string", @@ -83,66 +497,67 @@ var doc = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/models.JobGtidResp" + "$ref": "#/definitions/models.MysqlToKafkaJobDetailRespV2" } } } } }, - "/v2/job/migration": { + "/v2/job/subscription/pause": { "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "create or update migration job.", + "description": "pause subscription job.", "consumes": [ - "application/json" + "application/x-www-form-urlencoded" ], "tags": [ "job" ], - "operationId": "CreateOrUpdateMigrationJobV2", + "operationId": "PauseSubscriptionJobV2", "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/migration/detail": { - "get": { + "/v2/job/subscription/resume": { + "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "get migration job detail.", + "description": "resume subscription job.", + "consumes": [ + "application/x-www-form-urlencoded" + ], "tags": [ "job" ], - "operationId": "GetMigrationJobDetailV2", + "operationId": "ResumeSubscriptionJobV2", "parameters": [ { "type": "string", "description": "job id", "name": "job_id", - "in": "query", + "in": "formData", "required": true } ], @@ -150,27 +565,63 @@ var doc = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/models.MysqlToMysqlJobDetailRespV2" + "$ref": "#/definitions/models.ResumeJobRespV2" + } + } + } + } + }, + "/v2/job/sync/create": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "create sync job.", + "consumes": [ + "application/json" + ], + "tags": [ + "job" + ], + "operationId": "CreateSyncJobV2", + "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/job/pause": { + "/v2/job/sync/delete": { "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "pause job.", + "description": "delete sync job.", "consumes": [ "application/x-www-form-urlencoded" ], "tags": [ "job" ], - "operationId": "PauseJobV2", + "operationId": "DeleteSyncJobV2", "parameters": [ { "type": "string", @@ -184,33 +635,30 @@ var doc = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/models.PauseJobRespV2" + "$ref": "#/definitions/models.DeleteJobRespV2" } } } } }, - "/v2/job/resume": { - "post": { + "/v2/job/sync/detail": { + "get": { "security": [ { "ApiKeyAuth": [] } ], - "description": "resume job.", - "consumes": [ - "application/x-www-form-urlencoded" - ], + "description": "get sync job detail.", "tags": [ "job" ], - "operationId": "ResumeJobV2", + "operationId": "GetSyncJobDetailV2", "parameters": [ { "type": "string", "description": "job id", "name": "job_id", - "in": "formData", + "in": "query", "required": true } ], @@ -218,64 +666,61 @@ var doc = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/models.ResumeJobRespV2" + "$ref": "#/definitions/models.MysqlToMysqlJobDetailRespV2" } } } } }, - "/v2/job/reverse": { + "/v2/job/sync/pause": { "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "returnJob", + "description": "pause sync job.", "consumes": [ - "application/json" + "application/x-www-form-urlencoded" ], "tags": [ "job" ], - "operationId": "ReverseJobV2", + "operationId": "PauseSyncJobV2", "parameters": [ { - "description": "reverse config config", - "name": "reverse_config", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/models.ReverseJobReq" - } + "type": "string", + "description": "job id", + "name": "job_id", + "in": "formData", + "required": true } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/models.ReverseJobResp" + "$ref": "#/definitions/models.PauseJobRespV2" } } } } }, - "/v2/job/reverse_start": { + "/v2/job/sync/resume": { "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "Finish Job.", + "description": "resume sync job.", "consumes": [ "application/x-www-form-urlencoded" ], "tags": [ "job" ], - "summary": "start reverse-init job", - "operationId": "ReverseStartJobV2", + "operationId": "ResumeSyncJobV2", "parameters": [ { "type": "string", @@ -289,35 +734,35 @@ var doc = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/models.ReverseStartRespV2" + "$ref": "#/definitions/models.ResumeJobRespV2" } } } } }, - "/v2/job/subscription": { + "/v2/job/sync/reverse": { "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "create or update subscription job.", + "description": "reverse sync Job", "consumes": [ "application/json" ], "tags": [ "job" ], - "operationId": "CreateOrUpdateSubscriptionJobV2", + "operationId": "ReverseSyncJobV2", "parameters": [ { - "description": "subscription job config", - "name": "subscription_job_config", + "description": "reverse config config", + "name": "reverse_config", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/models.CreateOrUpdateMysqlToKafkaJobParamV2" + "$ref": "#/definitions/models.ReverseJobReq" } } ], @@ -325,30 +770,34 @@ var doc = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/models.CreateOrUpdateMysqlToKafkaJobRespV2" + "$ref": "#/definitions/models.ReverseJobResp" } } } } }, - "/v2/job/subscription/detail": { - "get": { + "/v2/job/sync/reverse_start": { + "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "get subscription job detail.", + "description": "Start Reverse Job.", + "consumes": [ + "application/x-www-form-urlencoded" + ], "tags": [ "job" ], - "operationId": "GetSubscriptionJobDetailV2", + "summary": "start reverse-init job", + "operationId": "ReverseStartSyncJobV2", "parameters": [ { "type": "string", "description": "job id", "name": "job_id", - "in": "query", + "in": "formData", "required": true } ], @@ -356,27 +805,27 @@ var doc = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/models.MysqlToKafkaJobDetailRespV2" + "$ref": "#/definitions/models.ReverseStartRespV2" } } } } }, - "/v2/job/sync": { + "/v2/job/sync/update": { "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "create or update sync job.", + "description": "update sync job.", "consumes": [ "application/json" ], "tags": [ "job" ], - "operationId": "CreateOrUpdateSyncJobV2", + "operationId": "UpdateSyncJobV2", "parameters": [ { "description": "sync job config", @@ -398,61 +847,111 @@ var doc = `{ } } }, - "/v2/job/sync/detail": { + "/v2/jobs/migration": { "get": { "security": [ { "ApiKeyAuth": [] } ], - "description": "get sync job detail.", + "description": "get job list.", "tags": [ "job" ], - "operationId": "GetSyncJobDetailV2", + "operationId": "MigrationJobListV2", "parameters": [ { "type": "string", - "description": "job id", - "name": "job_id", - "in": "query", - "required": true + "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.MysqlToMysqlJobDetailRespV2" + "$ref": "#/definitions/models.JobListRespV2" } } } } }, - "/v2/jobs": { + "/v2/jobs/subscription": { "get": { "security": [ { "ApiKeyAuth": [] } ], - "description": "get job list.", + "description": "get subscription job list.", "tags": [ "job" ], - "operationId": "JobListV2", + "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": [ - "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/sync": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "get sync job list.", + "tags": [ + "job" + ], + "operationId": "SyncJobListV2", + "parameters": [ { "type": "string", "description": "filter job id", @@ -1386,17 +1885,6 @@ var doc = `{ } } }, - "models.ActionItem": { - "type": "object", - "properties": { - "action": { - "type": "string" - }, - "uri": { - "type": "string" - } - } - }, "models.BasicTaskProfile": { "type": "object", "properties": { @@ -1461,6 +1949,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 +2474,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 +2512,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..d7f3c2664 100644 --- a/drivers/api/docs/swagger.json +++ b/drivers/api/docs/swagger.json @@ -8,21 +8,435 @@ }, "basePath": "/", "paths": { - "/v2/job/delete": { + "/v2/job/gtid": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "get src task current gtid.", + "tags": [ + "job" + ], + "operationId": "GetJobGtidV2", + "parameters": [ + { + "type": "string", + "description": "job id", + "name": "job_id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.JobGtidResp" + } + } + } + } + }, + "/v2/job/migration/create": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "create migration job.", + "consumes": [ + "application/json" + ], + "tags": [ + "job" + ], + "operationId": "CreateMigrationJobV2", + "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/migration/delete": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "delete migration job.", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "tags": [ + "job" + ], + "operationId": "DeleteMigrationJobV2", + "parameters": [ + { + "type": "string", + "description": "job id", + "name": "job_id", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.DeleteJobRespV2" + } + } + } + } + }, + "/v2/job/migration/detail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "get migration job detail.", + "tags": [ + "job" + ], + "operationId": "GetMigrationJobDetailV2", + "parameters": [ + { + "type": "string", + "description": "job id", + "name": "job_id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.MysqlToMysqlJobDetailRespV2" + } + } + } + } + }, + "/v2/job/migration/pause": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "pause migration job.", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "tags": [ + "job" + ], + "operationId": "PauseMigrationJobV2", + "parameters": [ + { + "type": "string", + "description": "job id", + "name": "job_id", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.PauseJobRespV2" + } + } + } + } + }, + "/v2/job/migration/resume": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "resume migration job.", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "tags": [ + "job" + ], + "operationId": "ResumeMigrationJobV2", + "parameters": [ + { + "type": "string", + "description": "job id", + "name": "job_id", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.ResumeJobRespV2" + } + } + } + } + }, + "/v2/job/migration/reverse": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "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" + ], + "summary": "start reverse-init job", + "operationId": "ReverseStartMigrationJobV2", + "parameters": [ + { + "type": "string", + "description": "job id", + "name": "job_id", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.ReverseStartRespV2" + } + } + } + } + }, + "/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/reverse_start": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Finish Job.", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "tags": [ + "job" + ], + "summary": "start reverse-init job", + "operationId": "ReverseStartJobV2", + "parameters": [ + { + "type": "string", + "description": "job id", + "name": "job_id", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.ReverseStartRespV2" + } + } + } + } + }, + "/v2/job/subscription": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "create or update subscription job.", + "consumes": [ + "application/json" + ], + "tags": [ + "job" + ], + "operationId": "CreateOrUpdateSubscriptionJobV2", + "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/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/delete": { "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "delete job.", + "description": "delete subscription job.", "consumes": [ "application/x-www-form-urlencoded" ], "tags": [ "job" ], - "operationId": "DeleteJobV2", + "operationId": "DeleteSubscriptionJobV2", "parameters": [ { "type": "string", @@ -42,18 +456,18 @@ } } }, - "/v2/job/gtid": { + "/v2/job/subscription/detail": { "get": { "security": [ { "ApiKeyAuth": [] } ], - "description": "get src task current gtid.", + "description": "get subscription job detail.", "tags": [ "job" ], - "operationId": "GetJobGtidV2", + "operationId": "GetSubscriptionJobDetailV2", "parameters": [ { "type": "string", @@ -67,66 +481,67 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/models.JobGtidResp" + "$ref": "#/definitions/models.MysqlToKafkaJobDetailRespV2" } } } } }, - "/v2/job/migration": { + "/v2/job/subscription/pause": { "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "create or update migration job.", + "description": "pause subscription job.", "consumes": [ - "application/json" + "application/x-www-form-urlencoded" ], "tags": [ "job" ], - "operationId": "CreateOrUpdateMigrationJobV2", + "operationId": "PauseSubscriptionJobV2", "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/migration/detail": { - "get": { + "/v2/job/subscription/resume": { + "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "get migration job detail.", + "description": "resume subscription job.", + "consumes": [ + "application/x-www-form-urlencoded" + ], "tags": [ "job" ], - "operationId": "GetMigrationJobDetailV2", + "operationId": "ResumeSubscriptionJobV2", "parameters": [ { "type": "string", "description": "job id", "name": "job_id", - "in": "query", + "in": "formData", "required": true } ], @@ -134,27 +549,63 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/models.MysqlToMysqlJobDetailRespV2" + "$ref": "#/definitions/models.ResumeJobRespV2" + } + } + } + } + }, + "/v2/job/sync/create": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "create sync job.", + "consumes": [ + "application/json" + ], + "tags": [ + "job" + ], + "operationId": "CreateSyncJobV2", + "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/job/pause": { + "/v2/job/sync/delete": { "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "pause job.", + "description": "delete sync job.", "consumes": [ "application/x-www-form-urlencoded" ], "tags": [ "job" ], - "operationId": "PauseJobV2", + "operationId": "DeleteSyncJobV2", "parameters": [ { "type": "string", @@ -168,33 +619,30 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/models.PauseJobRespV2" + "$ref": "#/definitions/models.DeleteJobRespV2" } } } } }, - "/v2/job/resume": { - "post": { + "/v2/job/sync/detail": { + "get": { "security": [ { "ApiKeyAuth": [] } ], - "description": "resume job.", - "consumes": [ - "application/x-www-form-urlencoded" - ], + "description": "get sync job detail.", "tags": [ "job" ], - "operationId": "ResumeJobV2", + "operationId": "GetSyncJobDetailV2", "parameters": [ { "type": "string", "description": "job id", "name": "job_id", - "in": "formData", + "in": "query", "required": true } ], @@ -202,64 +650,61 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/models.ResumeJobRespV2" + "$ref": "#/definitions/models.MysqlToMysqlJobDetailRespV2" } } } } }, - "/v2/job/reverse": { + "/v2/job/sync/pause": { "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "returnJob", + "description": "pause sync job.", "consumes": [ - "application/json" + "application/x-www-form-urlencoded" ], "tags": [ "job" ], - "operationId": "ReverseJobV2", + "operationId": "PauseSyncJobV2", "parameters": [ { - "description": "reverse config config", - "name": "reverse_config", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/models.ReverseJobReq" - } + "type": "string", + "description": "job id", + "name": "job_id", + "in": "formData", + "required": true } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/models.ReverseJobResp" + "$ref": "#/definitions/models.PauseJobRespV2" } } } } }, - "/v2/job/reverse_start": { + "/v2/job/sync/resume": { "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "Finish Job.", + "description": "resume sync job.", "consumes": [ "application/x-www-form-urlencoded" ], "tags": [ "job" ], - "summary": "start reverse-init job", - "operationId": "ReverseStartJobV2", + "operationId": "ResumeSyncJobV2", "parameters": [ { "type": "string", @@ -273,35 +718,35 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/models.ReverseStartRespV2" + "$ref": "#/definitions/models.ResumeJobRespV2" } } } } }, - "/v2/job/subscription": { + "/v2/job/sync/reverse": { "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "create or update subscription job.", + "description": "reverse sync Job", "consumes": [ "application/json" ], "tags": [ "job" ], - "operationId": "CreateOrUpdateSubscriptionJobV2", + "operationId": "ReverseSyncJobV2", "parameters": [ { - "description": "subscription job config", - "name": "subscription_job_config", + "description": "reverse config config", + "name": "reverse_config", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/models.CreateOrUpdateMysqlToKafkaJobParamV2" + "$ref": "#/definitions/models.ReverseJobReq" } } ], @@ -309,30 +754,34 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/models.CreateOrUpdateMysqlToKafkaJobRespV2" + "$ref": "#/definitions/models.ReverseJobResp" } } } } }, - "/v2/job/subscription/detail": { - "get": { + "/v2/job/sync/reverse_start": { + "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "get subscription job detail.", + "description": "Start Reverse Job.", + "consumes": [ + "application/x-www-form-urlencoded" + ], "tags": [ "job" ], - "operationId": "GetSubscriptionJobDetailV2", + "summary": "start reverse-init job", + "operationId": "ReverseStartSyncJobV2", "parameters": [ { "type": "string", "description": "job id", "name": "job_id", - "in": "query", + "in": "formData", "required": true } ], @@ -340,27 +789,27 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/models.MysqlToKafkaJobDetailRespV2" + "$ref": "#/definitions/models.ReverseStartRespV2" } } } } }, - "/v2/job/sync": { + "/v2/job/sync/update": { "post": { "security": [ { "ApiKeyAuth": [] } ], - "description": "create or update sync job.", + "description": "update sync job.", "consumes": [ "application/json" ], "tags": [ "job" ], - "operationId": "CreateOrUpdateSyncJobV2", + "operationId": "UpdateSyncJobV2", "parameters": [ { "description": "sync job config", @@ -382,61 +831,111 @@ } } }, - "/v2/job/sync/detail": { + "/v2/jobs/migration": { "get": { "security": [ { "ApiKeyAuth": [] } ], - "description": "get sync job detail.", + "description": "get job list.", "tags": [ "job" ], - "operationId": "GetSyncJobDetailV2", + "operationId": "MigrationJobListV2", "parameters": [ { "type": "string", - "description": "job id", - "name": "job_id", - "in": "query", - "required": true + "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.MysqlToMysqlJobDetailRespV2" + "$ref": "#/definitions/models.JobListRespV2" } } } } }, - "/v2/jobs": { + "/v2/jobs/subscription": { "get": { "security": [ { "ApiKeyAuth": [] } ], - "description": "get job list.", + "description": "get subscription job list.", "tags": [ "job" ], - "operationId": "JobListV2", + "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": [ - "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/sync": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "get sync job list.", + "tags": [ + "job" + ], + "operationId": "SyncJobListV2", + "parameters": [ { "type": "string", "description": "filter job id", @@ -1370,17 +1869,6 @@ } } }, - "models.ActionItem": { - "type": "object", - "properties": { - "action": { - "type": "string" - }, - "uri": { - "type": "string" - } - } - }, "models.BasicTaskProfile": { "type": "object", "properties": { @@ -1445,6 +1933,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 +2458,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 +2496,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..03a8ff24b 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: @@ -948,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. @@ -988,12 +992,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 @@ -1010,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. @@ -1029,12 +1054,12 @@ paths: - ApiKeyAuth: [] tags: - job - /v2/job/pause: + /v2/job/migration/pause: post: consumes: - application/x-www-form-urlencoded - description: pause job. - operationId: PauseJobV2 + description: pause migration job. + operationId: PauseMigrationJobV2 parameters: - description: job id in: formData @@ -1050,12 +1075,12 @@ paths: - ApiKeyAuth: [] tags: - job - /v2/job/resume: + /v2/job/migration/resume: post: consumes: - application/x-www-form-urlencoded - description: resume job. - operationId: ResumeJobV2 + description: resume migration job. + operationId: ResumeMigrationJobV2 parameters: - description: job id in: formData @@ -1071,12 +1096,12 @@ paths: - ApiKeyAuth: [] tags: - job - /v2/job/reverse: + /v2/job/migration/reverse: post: consumes: - application/json - description: returnJob - operationId: ReverseJobV2 + description: reverse migration Job + operationId: ReverseMigrationJobV2 parameters: - description: reverse config config in: body @@ -1093,6 +1118,50 @@ paths: - ApiKeyAuth: [] tags: - job + /v2/job/migration/reverse_start: + post: + consumes: + - application/x-www-form-urlencoded + description: Start Reverse Job. + operationId: ReverseStartMigrationJobV2 + 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/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/reverse_start: post: consumes: @@ -1137,6 +1206,49 @@ 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/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. @@ -1156,12 +1268,54 @@ paths: - ApiKeyAuth: [] tags: - job - /v2/job/sync: + /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: - application/json - description: create or update sync job. - operationId: CreateOrUpdateSyncJobV2 + description: create sync job. + operationId: CreateSyncJobV2 parameters: - description: sync job config in: body @@ -1178,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. @@ -1197,19 +1372,177 @@ paths: - ApiKeyAuth: [] tags: - job - /v2/jobs: + /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: + - 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/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 c324f5ac2..faa495493 100644 --- a/drivers/api/handler/v2/job.go +++ b/drivers/api/handler/v2/job.go @@ -28,21 +28,50 @@ 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 { - return err + return c.JSON(http.StatusInternalServerError, models.BuildBaseResp(err)) } user, err := getCurrentUser(c) @@ -66,8 +95,8 @@ 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) - if "" != reqParam.FilterJobType && reqParam.FilterJobType != string(jobType) { + jobType := GetJobTypeFromJobId(consulJob.JobId) + if filterJobType != jobType { continue } if !userHasAccess(storeManager, consulJob.User, user) { @@ -139,7 +168,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 +183,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 +205,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 +533,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 +706,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 +876,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 +896,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 +932,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 +953,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 +1086,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) @@ -1098,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/pause [post] -func PauseJobV2(c echo.Context) error { +// @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/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 err + 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)) @@ -1178,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/resume [post] -func ResumeJobV2(c echo.Context) error { +// @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/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 err + 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)) @@ -1274,19 +1405,50 @@ 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/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/delete [post] -func DeleteJobV2(c echo.Context) error { +// @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 err + 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 { @@ -1328,7 +1490,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) @@ -1351,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 @@ -1360,11 +1548,14 @@ 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 err + 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 { @@ -1372,7 +1563,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)) } @@ -1429,20 +1620,39 @@ 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/reverse [post] -func ReverseJobV2(c echo.Context) error { +// @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/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 err + 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 { @@ -1459,7 +1669,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 +1781,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..4308463b7 100644 --- a/drivers/api/handler/v2/login.go +++ b/drivers/api/handler/v2/login.go @@ -31,9 +31,14 @@ 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)) + } + 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("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) 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)) } @@ -82,7 +87,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 9fa209df7..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,9 +65,12 @@ 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) { + 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, @@ -91,10 +94,14 @@ 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.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, @@ -143,16 +150,31 @@ 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.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..acc8191ae 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,11 @@ 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)) + } + 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 { @@ -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", 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)) } @@ -265,12 +269,17 @@ 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 { 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))) @@ -363,7 +372,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 aa5ef16db..609b77c34 100644 --- a/drivers/api/route.go +++ b/drivers/api/route.go @@ -82,26 +82,42 @@ 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) - v2Router.POST("/log/level", v2.UpdateLogLevelV2, AdminUserAllowed()) - v2Router.GET("/jobs", v2.JobListV2) + e.GET("/v2/monitor/task", v2.GetTaskProgressV2) + v2Router.POST("/log/level", v2.UpdateLogLevelV2) + v2Router.GET("/jobs/migration", v2.MigrationJobListV2) 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.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", v2.CreateOrUpdateSyncJobV2) + 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", v2.CreateOrUpdateSubscriptionJobV2) - v2Router.POST("/job/pause", v2.PauseJobV2) - v2Router.POST("/job/resume", v2.ResumeJobV2) - v2Router.POST("/job/delete", v2.DeleteJobV2) + v2Router.POST("/job/subscription/create", v2.CreateSubscriptionJobV2) + v2Router.POST("/job/subscription/update", v2.UpdateSubscriptionJobV2) + 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("/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.POST("/user/update", v2.UpdateUserV2) @@ -111,9 +127,9 @@ func SetupApiServer(logger hclog.Logger, driverConfig *mysql.DriverConfig) (err v2Router.GET("/user/current_user", v2.GetCurrentUserV2) v2Router.GET("/user/list_action", v2.ListActionV2) v2Router.GET("/role/list", v2.RoleListV2) - v2Router.POST("/role/create", v2.CreateRoleV2, AdminUserAllowed()) - v2Router.POST("/role/delete", v2.DeleteRoleV2, AdminUserAllowed()) - v2Router.POST("/role/update", v2.UpdateRoleV2, AdminUserAllowed()) + 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)) @@ -184,6 +200,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 { @@ -202,14 +221,14 @@ 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") + return echo.NewHTTPError(http.StatusForbidden, fmt.Sprintf("check your authority fail : %v", err)) } - 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 { return next(c) } } @@ -219,25 +238,25 @@ 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("ResumeJobV2") - 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) +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!" diff --git a/drivers/mysql/common/common.go b/drivers/mysql/common/common.go index 3fb370e2b..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 = "{\"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\"}]}" - // 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 ( 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 {