From 8d3cc5646ffd40db2a3045f18dd79faa2085dbe4 Mon Sep 17 00:00:00 2001 From: kimchangyu-25 Date: Fri, 28 Nov 2025 07:44:30 +0000 Subject: [PATCH 1/6] refactor: alibaba endpoint change to string templates instead of static strings --- config/provider.go | 8 ++--- internal/auth/base.go | 5 ++- pkg/objectstorage/alibabafs/alibabafs.go | 46 +++++++++++++++++++----- web/js/scripts.js | 39 ++------------------ web/templates/back-backup.html | 2 ++ websrc/routes/routes.go | 1 + 6 files changed, 48 insertions(+), 53 deletions(-) diff --git a/config/provider.go b/config/provider.go index 406175f..ee5cf5b 100644 --- a/config/provider.go +++ b/config/provider.go @@ -160,15 +160,13 @@ func NewGCPClient(credentialsJson string) (*storage.Client, error) { return client, nil } -func NewAlibabaClient(endpoint, region, accessKey, secretKey string) (*oss.Client, error) { - if endpoint == "" { - return nil, errors.New("endpoint is required") - } +func NewAlibabaClient(region, accessKey, secretKey string) (*oss.Client, error) { + if accessKey == "" || secretKey == "" { return nil, errors.New("accessKey and secretKey are required") } cfg := oss.LoadDefaultConfig(). - WithEndpoint(endpoint). + WithEndpoint("https://oss-" + region + ".aliyuncs.com"). WithCredentialsProvider(osscred.NewStaticCredentialsProvider(accessKey, secretKey)). WithRegion(region). WithRetryMaxAttempts(5) diff --git a/internal/auth/base.go b/internal/auth/base.go index 7e92f66..77f5620 100644 --- a/internal/auth/base.go +++ b/internal/auth/base.go @@ -194,16 +194,15 @@ func GetOS(params *models.ProviderConfig) (*osc.OSController, error) { return nil, errors.New("credential load failed") } - log.Info().Str("Endpoint", params.Endpoint).Msg("Alibaba Credentials") log.Info().Str("Region", params.Region).Msg("Alibaba Region") log.Info().Str("AccessKey", alibabac.AccessKey).Msg("Alibaba Credentials") log.Info().Str("SecretKey", alibabac.SecretKey).Msg("Alibaba Credentials") log.Info().Str("BucketName", params.Bucket).Msg("Alibaba BucketName") - ossc, err := config.NewAlibabaClient(params.Endpoint, params.Region, alibabac.AccessKey, alibabac.SecretKey) + ossc, err := config.NewAlibabaClient(params.Region, alibabac.AccessKey, alibabac.SecretKey) if err != nil { return nil, fmt.Errorf("NewAlibabaClient error : %v", err) } - OSC, err = osc.New(alibabafs.New(models.ALIBABA, ossc, params.Endpoint, params.Bucket, params.Region)) + OSC, err = osc.New(alibabafs.New(models.ALIBABA, ossc, "https://oss-"+params.Region+".aliyuncs.com", params.Bucket, params.Region)) if err != nil { return nil, fmt.Errorf("osc error : %v", err) } diff --git a/pkg/objectstorage/alibabafs/alibabafs.go b/pkg/objectstorage/alibabafs/alibabafs.go index 6be0408..1a39030 100644 --- a/pkg/objectstorage/alibabafs/alibabafs.go +++ b/pkg/objectstorage/alibabafs/alibabafs.go @@ -18,6 +18,7 @@ package alibabafs import ( "context" "encoding/json" + "encoding/xml" "errors" "fmt" "io" @@ -135,8 +136,29 @@ func (f *AlibabaFS) DeleteBucket() error { // deleteObjectBatch deletes objects in manageable chunks. func (f *AlibabaFS) deleteObjectBatch(keys []string) error { - // TODO: marshal keys into the request body and invoke the OpenAPI client. - return ErrNotImplemented + path := "/tumblebug/resources/objectStorage/" + f.bucketName + "?delete=true" + method := http.MethodPost + connName := fmt.Sprintf("%s-%s", f.provider, f.region) + + deleteReq := models.DeleteRequest{ + XMLNS: "http://s3.amazonaws.com/doc/2006-03-01/", + } + for _, key := range keys { + deleteReq.Objects = append(deleteReq.Objects, models.S3Object{Key: key}) + } + // 보기 좋게 들여쓰기된 XML 생성 + output, err := xml.MarshalIndent(deleteReq, "", " ") + if err != nil { + return err + } + + // XML 헤더 추가 + _, rerr := utils.RequestTumblebug(path, method, connName, []byte(xml.Header+string(output))) + if rerr != nil { + return err + } + + return nil } // ObjectList yields the objects contained within the configured bucket. @@ -227,16 +249,24 @@ func (f *AlibabaFS) BucketList() ([]models.Bucket, error) { // Open streams a single object from Alibaba Cloud OSS. func (f *AlibabaFS) Open(name string) (io.ReadCloser, error) { - // TODO: wire Alibaba download stream into an io.Reader. - return nil, ErrNotImplemented + ctx := f.ctx + if ctx == nil { + ctx = context.Background() + } + + result, err := f.client.GetObject(ctx, &oss.GetObjectRequest{ + Bucket: oss.Ptr(f.bucketName), + Key: oss.Ptr(name), + }) + if err != nil { + return nil, err + } + + return result.Body, nil } // Create opens a writer that uploads an object to the configured bucket. func (f *AlibabaFS) Create(name string) (io.WriteCloser, error) { - if f.client == nil { - return nil, fmt.Errorf("alibaba oss client is not configured") - } - ctx := f.ctx if ctx == nil { ctx = context.Background() diff --git a/web/js/scripts.js b/web/js/scripts.js index da86e4a..2d47a90 100644 --- a/web/js/scripts.js +++ b/web/js/scripts.js @@ -139,36 +139,9 @@ function resolveAlibabaEndpoint(region) { return ""; } const normalized = region.trim().toLowerCase(); - return ALIBABA_ENDPOINTS[normalized] || `https://oss-${normalized}.aliyuncs.com`; + return `https://oss-${normalized}.aliyuncs.com`; } -function emitAlibabaRegionEvent(prefix, provider, region) { - const normalized = region && region !== "none" ? region : null; - const detail = { - prefix, - provider, - region: normalized, - }; - document.dispatchEvent(new CustomEvent("alibabaRegionChange", { detail })); -} - -document.addEventListener("alibabaRegionChange", (event) => { - const { prefix, provider, region } = event.detail; - const hint = document.getElementById(`${prefix}AlibabaRegionHint`); - const endpointInput = document.getElementById(`${prefix}Point[endpoint]`); - - if (!endpointInput) { - return; - } - - if (provider === "alibaba") { - endpointInput.value = region ? resolveAlibabaEndpoint(region) : ""; - } else { - endpointInput.value = ""; - } - endpointInput.dispatchEvent(new Event("change")); -}); - function generateFormSubmit() { const form = document.getElementById('genForm'); @@ -1042,7 +1015,7 @@ document.addEventListener("DOMContentLoaded", () => { const initProviderHandlers = (prefix) => { const credSelect = document.getElementById(prefix + "CredentialSelect"); const providerInput = document.getElementById(prefix + "Point[provider]"); - const regionSelect = document.getElementById(prefix + "RegionSelect"); + //const regionSelect = document.getElementById(prefix + "RegionSelect"); if (!credSelect || !providerInput) return; // 초기 렌더링 시 region 갱신 @@ -1050,7 +1023,6 @@ document.addEventListener("DOMContentLoaded", () => { providerInput.value = initialProvider; updateRegionsByProvider(prefix, initialProvider); updateLabelByProvider(prefix, initialProvider); - emitAlibabaRegionEvent(prefix, initialProvider, regionSelect?.value || null); // 이벤트 핸들러 등록 credSelect.addEventListener("change", (e) => { @@ -1059,14 +1031,7 @@ document.addEventListener("DOMContentLoaded", () => { providerInput.dispatchEvent(new Event('change')); updateRegionsByProvider(prefix, provider); updateLabelByProvider(prefix, provider); - emitAlibabaRegionEvent(prefix, provider, regionSelect?.value || null); }); - - if (regionSelect) { - regionSelect.addEventListener("change", () => { - emitAlibabaRegionEvent(prefix, providerInput.value, regionSelect.value); - }); - } }; // source/target 공통 처리 diff --git a/web/templates/back-backup.html b/web/templates/back-backup.html index 9c18505..bcd2827 100644 --- a/web/templates/back-backup.html +++ b/web/templates/back-backup.html @@ -90,6 +90,7 @@

Object Storage

+
@@ -233,6 +234,7 @@

Object Storage

aws: "AWS S3", ncp: "Naver Object Storage", gcp: "Google Cloud Storage", + alibaba: "Alibaba Object Storage", }, rdbms: { default: "MySQL", diff --git a/websrc/routes/routes.go b/websrc/routes/routes.go index c751b8c..9e0eab0 100644 --- a/websrc/routes/routes.go +++ b/websrc/routes/routes.go @@ -50,6 +50,7 @@ func GenerateRoutes(g *echo.Group) { // g.POST("/firestore", controllers.GenerateFirestorePostHandler) g.GET("/mongodb", controllers.GenerateMongoDBGetHandler) + //g.GET("/tablestorage", controllers.GenerateTableStorageGetHandler) g.GET("/credential", controllers.GenerateCredentialGetHandler) // g.POST("/mongodb", controllers.GenerateMongoDBPostHandler) From cb47fdca3a807419e15481a54fafd1160e4a7afe Mon Sep 17 00:00:00 2001 From: coli-bear Date: Fri, 17 Apr 2026 18:13:15 +0900 Subject: [PATCH 2/6] =?UTF-8?q?feat:=20alibaba=20nosql=20gen/mig/backup/re?= =?UTF-8?q?store=20=EA=B5=AC=ED=98=84=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO.md | 20 + config/provider.go | 19 + data/var/run/data-manager/task/task.json | 681 +++++++++++++++++++++-- internal/auth/base.go | 19 + pkg/nrdbms/alibabamgdb/alibabamgdb.go | 108 ++++ web/js/scripts.js | 7 +- web/templates/back-backup.html | 18 +- web/templates/gen-no-sql.html | 10 +- web/templates/mig-no-sql.html | 14 +- web/templates/res-restore.html | 7 +- websrc/controllers/pageHandlers.go | 26 +- websrc/controllers/publicfunc.go | 6 +- 12 files changed, 857 insertions(+), 78 deletions(-) create mode 100644 TODO.md create mode 100644 pkg/nrdbms/alibabamgdb/alibabamgdb.go diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..e6a2077 --- /dev/null +++ b/TODO.md @@ -0,0 +1,20 @@ +## TODO1. Alibaba Cloud NoSQL(MongoDB) + +- Alibaba Cloud NoSQL (MongoDB) 지원 추가 + +### 적용 기능 범위 + +- Generate +- Backup +- Restore +- Migration + +### 적용 범위 + +- Web UI +- Backend API + +### 참고사항 + +- Alibaba Cloud MongoDB 는 NCloud 에서 제공하는 MongoDB 와 동일 한 형식으로 제공 + diff --git a/config/provider.go b/config/provider.go index 406175f..fb071a6 100644 --- a/config/provider.go +++ b/config/provider.go @@ -102,6 +102,25 @@ func NewNCPMongoDBClient(username, password, host string, port int) (*mongo.Clie return mongo.Connect(context.Background(), newNCPMongoDBConfig(username, password, host, port)) } +func newAlibabaMongoDBConfig(username, password, host string, port int) *options.ClientOptions { + dc := true + return &options.ClientOptions{ + Auth: &options.Credential{ + Username: username, + Password: password, + }, + Direct: &dc, + Hosts: []string{fmt.Sprintf("%s:%d", host, port)}, + } +} + +func NewAlibabaMongoDBClient(username, password, host string, port int) (*mongo.Client, error) { + if err := validateInputs(&username, &password, &host, &port); err != nil { + return nil, err + } + return mongo.Connect(context.Background(), newAlibabaMongoDBConfig(username, password, host, port)) +} + func NewS3Client(accesskey, secretkey, region string) (*s3.Client, error) { cfg, err := newAWSConfig(accesskey, secretkey, region) if err != nil { diff --git a/data/var/run/data-manager/task/task.json b/data/var/run/data-manager/task/task.json index f890e2c..a971a64 100644 --- a/data/var/run/data-manager/task/task.json +++ b/data/var/run/data-manager/task/task.json @@ -5421,17 +5421,19 @@ "projectId": "" }, "sourceFilter": { - "prefix": "datamold-dummy2244858493/sql/", + "path": "", + "pathExcludeYn": "", "contains": null, + "containExcludeYn": "", "suffixes": null, "exact": [ "LibraryManagement_999.sql" ], - "regex": "", "minSize": null, "maxSize": null, "modifiedAfter": null, - "modifiedBefore": null + "modifiedBefore": null, + "sizeFilteringUnit": "" } }, { @@ -5493,17 +5495,19 @@ "projectId": "" }, "sourceFilter": { - "prefix": "datamold-dummy2244858493/sql/", + "path": "", + "pathExcludeYn": "", "contains": null, + "containExcludeYn": "", "suffixes": null, "exact": [ "LibraryManagement_999.sql" ], - "regex": "", "minSize": null, "maxSize": null, "modifiedAfter": null, - "modifiedBefore": null + "modifiedBefore": null, + "sizeFilteringUnit": "" } }, { @@ -5565,17 +5569,19 @@ "projectId": "" }, "sourceFilter": { - "prefix": "datamold-dummy2244858493/sql/", + "path": "", + "pathExcludeYn": "", "contains": null, + "containExcludeYn": "", "suffixes": null, "exact": [ "LibraryManagement_999.sql" ], - "regex": "", "minSize": null, "maxSize": null, "modifiedAfter": null, - "modifiedBefore": null + "modifiedBefore": null, + "sizeFilteringUnit": "" } }, { @@ -5637,17 +5643,19 @@ "projectId": "" }, "sourceFilter": { - "prefix": "datamold-dummy2244858493/sql/", + "path": "", + "pathExcludeYn": "", "contains": null, + "containExcludeYn": "", "suffixes": null, "exact": [ "LibraryManagement_999.sql" ], - "regex": "", "minSize": null, "maxSize": null, "modifiedAfter": null, - "modifiedBefore": null + "modifiedBefore": null, + "sizeFilteringUnit": "" } }, { @@ -5709,17 +5717,19 @@ "projectId": "" }, "sourceFilter": { - "prefix": "datamold-dummy2244858493/sql/", + "path": "", + "pathExcludeYn": "", "contains": null, + "containExcludeYn": "", "suffixes": null, "exact": [ "LibraryManagement_999.sql" ], - "regex": "", "minSize": null, "maxSize": null, "modifiedAfter": null, - "modifiedBefore": null + "modifiedBefore": null, + "sizeFilteringUnit": "" } }, { @@ -5781,17 +5791,19 @@ "projectId": "" }, "sourceFilter": { - "prefix": "datamold-dummy2244858493/sql/", + "path": "", + "pathExcludeYn": "", "contains": null, + "containExcludeYn": "", "suffixes": null, "exact": [ "LibraryManagement_999.sql" ], - "regex": "", "minSize": null, "maxSize": null, "modifiedAfter": null, - "modifiedBefore": null + "modifiedBefore": null, + "sizeFilteringUnit": "" } }, { @@ -5853,19 +5865,21 @@ "projectId": "" }, "sourceFilter": { - "prefix": "datamold-dummy2244858493/sql/", + "path": "", + "pathExcludeYn": "", "contains": null, + "containExcludeYn": "", "suffixes": null, "exact": [ "LibraryManagement_991.sql", "LibraryManagement_993.sql", "LibraryManagement_994.sql" ], - "regex": "", "minSize": null, "maxSize": null, "modifiedAfter": null, - "modifiedBefore": null + "modifiedBefore": null, + "sizeFilteringUnit": "" } }, { @@ -5927,19 +5941,21 @@ "projectId": "" }, "sourceFilter": { - "prefix": "datamold-dummy2244858493/sql/", + "path": "", + "pathExcludeYn": "", "contains": null, + "containExcludeYn": "", "suffixes": null, "exact": [ "LibraryManagement_991.sql", "LibraryManagement_993.sql", "LibraryManagement_994.sql" ], - "regex": "", "minSize": null, "maxSize": null, "modifiedAfter": null, - "modifiedBefore": null + "modifiedBefore": null, + "sizeFilteringUnit": "" } }, { @@ -6001,15 +6017,17 @@ "projectId": "" }, "sourceFilter": { - "prefix": "datamold-dummy2244858493/sql/", + "path": "", + "pathExcludeYn": "", "contains": null, + "containExcludeYn": "", "suffixes": null, "exact": null, - "regex": "", "minSize": null, "maxSize": null, "modifiedAfter": null, - "modifiedBefore": null + "modifiedBefore": null, + "sizeFilteringUnit": "" } }, { @@ -6248,15 +6266,17 @@ "projectId": "" }, "sourceFilter": { - "prefix": "datamold-dummy2244858493/test1234/", + "path": "", + "pathExcludeYn": "", "contains": null, + "containExcludeYn": "", "suffixes": null, "exact": null, - "regex": "", "minSize": null, "maxSize": null, "modifiedAfter": null, - "modifiedBefore": null + "modifiedBefore": null, + "sizeFilteringUnit": "" } }, { @@ -6318,15 +6338,17 @@ "projectId": "" }, "sourceFilter": { - "prefix": "test1234/", + "path": "", + "pathExcludeYn": "", "contains": null, + "containExcludeYn": "", "suffixes": null, "exact": null, - "regex": "", "minSize": null, "maxSize": null, "modifiedAfter": null, - "modifiedBefore": null + "modifiedBefore": null, + "sizeFilteringUnit": "" } }, { @@ -6388,15 +6410,17 @@ "projectId": "" }, "sourceFilter": { - "prefix": "datamold-dummy2244858493/sql", + "path": "", + "pathExcludeYn": "", "contains": null, + "containExcludeYn": "", "suffixes": null, "exact": null, - "regex": "", "minSize": null, "maxSize": null, "modifiedAfter": null, - "modifiedBefore": null + "modifiedBefore": null, + "sizeFilteringUnit": "" } }, { @@ -6458,15 +6482,17 @@ "projectId": "" }, "sourceFilter": { - "prefix": "datamold-dummy2244858493/sql", + "path": "", + "pathExcludeYn": "", "contains": null, + "containExcludeYn": "", "suffixes": null, "exact": null, - "regex": "", "minSize": null, "maxSize": null, "modifiedAfter": null, - "modifiedBefore": null + "modifiedBefore": null, + "sizeFilteringUnit": "" } }, { @@ -6528,19 +6554,21 @@ "projectId": "" }, "sourceFilter": { - "prefix": "datamold-dummy2244858493/sql", + "path": "", + "pathExcludeYn": "", "contains": null, + "containExcludeYn": "", "suffixes": null, "exact": [ "LibraryManagement_991.sql", "LibraryManagement_993.sql", "LibraryManagement_994.sql" ], - "regex": "", "minSize": null, "maxSize": null, "modifiedAfter": null, - "modifiedBefore": null + "modifiedBefore": null, + "sizeFilteringUnit": "" } }, { @@ -6602,15 +6630,17 @@ "projectId": "" }, "sourceFilter": { - "prefix": "datamold-dummy2244858493/sql", + "path": "", + "pathExcludeYn": "", "contains": null, + "containExcludeYn": "", "suffixes": null, "exact": null, - "regex": "", "minSize": null, "maxSize": null, "modifiedAfter": null, - "modifiedBefore": null + "modifiedBefore": null, + "sizeFilteringUnit": "" } }, { @@ -6672,17 +6702,19 @@ "projectId": "" }, "sourceFilter": { - "prefix": "datamold-dummy2244858493/sql", + "path": "", + "pathExcludeYn": "", "contains": null, + "containExcludeYn": "", "suffixes": null, "exact": [ "LibraryManagement_991.sql" ], - "regex": "", "minSize": null, "maxSize": null, "modifiedAfter": null, - "modifiedBefore": null + "modifiedBefore": null, + "sizeFilteringUnit": "" } }, { @@ -6744,15 +6776,17 @@ "projectId": "" }, "sourceFilter": { - "prefix": "datamold-dummy2244858493/sql", + "path": "", + "pathExcludeYn": "", "contains": null, + "containExcludeYn": "", "suffixes": null, "exact": null, - "regex": "", "minSize": null, "maxSize": null, "modifiedAfter": null, - "modifiedBefore": null + "modifiedBefore": null, + "sizeFilteringUnit": "" } }, { @@ -6814,17 +6848,556 @@ "projectId": "" }, "sourceFilter": { - "prefix": "", + "path": "", + "pathExcludeYn": "", "contains": null, + "containExcludeYn": "", "suffixes": [ "png" ], "exact": null, - "regex": "", "minSize": null, "maxSize": null, "modifiedAfter": null, - "modifiedBefore": null + "modifiedBefore": null, + "sizeFilteringUnit": "" + } + }, + { + "meta": { + "serviceType": "nrdbms", + "taskType": "generate", + "taskId": "-task-0-20260417-154555" + }, + "dummy": { + "dummyPath": "/var/folders/8d/dykp7pg5693fhf5_72hr16540000gn/T/datamold-dummy681617525", + "checkSQL": false, + "checkCSV": false, + "checkTXT": false, + "checkPNG": false, + "checkGIF": false, + "checkZIP": false, + "checkJSON": false, + "checkXML": false, + "checkServerJSON": true, + "checkServerSQL": false, + "sizeSQL": "", + "sizeCSV": "", + "sizeTXT": "", + "sizePNG": "", + "sizeGIF": "", + "sizeZIP": "", + "sizeJSON": "", + "sizeXML": "", + "sizeServerJSON": "1", + "sizeServerSQL": "" + }, + "sourcePoint": { + "provider": "", + "region": "", + "credentialId": 0, + "path": "", + "bucket": "", + "endpoint": "", + "host": "", + "port": "", + "username": "", + "password": "", + "databaseName": "", + "databaseId": "", + "projectId": "" + }, + "targetPoint": { + "provider": "alibaba", + "region": "ap-northeast-1", + "credentialId": 1, + "path": "", + "bucket": "", + "endpoint": "https://oss-ap-northeast-1.aliyuncs.com", + "host": "dds-mj76dbe602390fa42.mongodb.ap-northeast-2.rds.aliyuncs.com", + "port": "3717", + "username": "root", + "password": "ㅂwe123!@#", + "databaseName": "admin", + "databaseId": "", + "projectId": "" + } + }, + { + "meta": { + "serviceType": "nrdbms", + "taskType": "generate", + "taskId": "-task-0-20260417-154747" + }, + "dummy": { + "dummyPath": "/var/folders/8d/dykp7pg5693fhf5_72hr16540000gn/T/datamold-dummy1196488898", + "checkSQL": false, + "checkCSV": false, + "checkTXT": false, + "checkPNG": false, + "checkGIF": false, + "checkZIP": false, + "checkJSON": false, + "checkXML": false, + "checkServerJSON": true, + "checkServerSQL": false, + "sizeSQL": "", + "sizeCSV": "", + "sizeTXT": "", + "sizePNG": "", + "sizeGIF": "", + "sizeZIP": "", + "sizeJSON": "", + "sizeXML": "", + "sizeServerJSON": "1", + "sizeServerSQL": "" + }, + "sourcePoint": { + "provider": "", + "region": "", + "credentialId": 0, + "path": "", + "bucket": "", + "endpoint": "", + "host": "", + "port": "", + "username": "", + "password": "", + "databaseName": "", + "databaseId": "", + "projectId": "" + }, + "targetPoint": { + "provider": "alibaba", + "region": "ap-northeast-1", + "credentialId": 1, + "path": "", + "bucket": "", + "endpoint": "https://oss-ap-northeast-1.aliyuncs.com", + "host": "dds-mj76dbe602390fa41311-pub.mongodb.ap-northeast-2.rds.aliyuncs.com", + "port": "3717", + "username": "root", + "password": "ㅂwe123!@#", + "databaseName": "database", + "databaseId": "", + "projectId": "" + } + }, + { + "meta": { + "serviceType": "nrdbms", + "taskType": "generate", + "taskId": "-task-0-20260417-155248" + }, + "dummy": { + "dummyPath": "/var/folders/8d/dykp7pg5693fhf5_72hr16540000gn/T/datamold-dummy3618700754", + "checkSQL": false, + "checkCSV": false, + "checkTXT": false, + "checkPNG": false, + "checkGIF": false, + "checkZIP": false, + "checkJSON": false, + "checkXML": false, + "checkServerJSON": true, + "checkServerSQL": false, + "sizeSQL": "", + "sizeCSV": "", + "sizeTXT": "", + "sizePNG": "", + "sizeGIF": "", + "sizeZIP": "", + "sizeJSON": "", + "sizeXML": "", + "sizeServerJSON": "1", + "sizeServerSQL": "" + }, + "sourcePoint": { + "provider": "", + "region": "", + "credentialId": 0, + "path": "", + "bucket": "", + "endpoint": "", + "host": "", + "port": "", + "username": "", + "password": "", + "databaseName": "", + "databaseId": "", + "projectId": "" + }, + "targetPoint": { + "provider": "alibaba", + "region": "ap-northeast-1", + "credentialId": 1, + "path": "", + "bucket": "", + "endpoint": "https://oss-ap-northeast-1.aliyuncs.com", + "host": "dds-mj76dbe602390fa41311-pub.mongodb.ap-northeast-2.rds.aliyuncs.com", + "port": "3717", + "username": "root", + "password": "qwe123!@#", + "databaseName": "database", + "databaseId": "", + "projectId": "" + } + }, + { + "meta": { + "serviceType": "nrdbms", + "taskType": "generate", + "taskId": "-task-0-20260417-161821" + }, + "dummy": { + "dummyPath": "/var/folders/8d/dykp7pg5693fhf5_72hr16540000gn/T/datamold-dummy2021042541", + "checkSQL": false, + "checkCSV": false, + "checkTXT": false, + "checkPNG": false, + "checkGIF": false, + "checkZIP": false, + "checkJSON": false, + "checkXML": false, + "checkServerJSON": true, + "checkServerSQL": false, + "sizeSQL": "", + "sizeCSV": "", + "sizeTXT": "", + "sizePNG": "", + "sizeGIF": "", + "sizeZIP": "", + "sizeJSON": "", + "sizeXML": "", + "sizeServerJSON": "1", + "sizeServerSQL": "" + }, + "sourcePoint": { + "provider": "", + "region": "", + "credentialId": 0, + "path": "", + "bucket": "", + "endpoint": "", + "host": "", + "port": "", + "username": "", + "password": "", + "databaseName": "", + "databaseId": "", + "projectId": "" + }, + "targetPoint": { + "provider": "aws", + "region": "ap-northeast-1", + "credentialId": 2, + "path": "", + "bucket": "", + "endpoint": "", + "host": "", + "port": "", + "username": "", + "password": "", + "databaseName": "", + "databaseId": "", + "projectId": "" + } + }, + { + "meta": { + "serviceType": "nrdbms", + "taskType": "generate", + "taskId": "-task-0-20260417-162010" + }, + "dummy": { + "dummyPath": "/var/folders/8d/dykp7pg5693fhf5_72hr16540000gn/T/datamold-dummy1949661756", + "checkSQL": false, + "checkCSV": false, + "checkTXT": false, + "checkPNG": false, + "checkGIF": false, + "checkZIP": false, + "checkJSON": false, + "checkXML": false, + "checkServerJSON": true, + "checkServerSQL": false, + "sizeSQL": "", + "sizeCSV": "", + "sizeTXT": "", + "sizePNG": "", + "sizeGIF": "", + "sizeZIP": "", + "sizeJSON": "", + "sizeXML": "", + "sizeServerJSON": "1", + "sizeServerSQL": "" + }, + "sourcePoint": { + "provider": "", + "region": "", + "credentialId": 0, + "path": "", + "bucket": "", + "endpoint": "", + "host": "", + "port": "", + "username": "", + "password": "", + "databaseName": "", + "databaseId": "", + "projectId": "" + }, + "targetPoint": { + "provider": "aws", + "region": "ap-northeast-2", + "credentialId": 2, + "path": "", + "bucket": "", + "endpoint": "", + "host": "", + "port": "", + "username": "", + "password": "", + "databaseName": "", + "databaseId": "", + "projectId": "" + } + }, + { + "meta": { + "serviceType": "nrdbms", + "taskType": "generate", + "taskId": "-task-0-20260417-173239" + }, + "dummy": { + "dummyPath": "/var/folders/8d/dykp7pg5693fhf5_72hr16540000gn/T/datamold-dummy285215639", + "checkSQL": false, + "checkCSV": false, + "checkTXT": false, + "checkPNG": false, + "checkGIF": false, + "checkZIP": false, + "checkJSON": false, + "checkXML": false, + "checkServerJSON": true, + "checkServerSQL": false, + "sizeSQL": "", + "sizeCSV": "", + "sizeTXT": "", + "sizePNG": "", + "sizeGIF": "", + "sizeZIP": "", + "sizeJSON": "", + "sizeXML": "", + "sizeServerJSON": "1", + "sizeServerSQL": "" + }, + "sourcePoint": { + "provider": "", + "region": "", + "credentialId": 0, + "path": "", + "bucket": "", + "endpoint": "", + "host": "", + "port": "", + "username": "", + "password": "", + "databaseName": "", + "databaseId": "", + "projectId": "" + }, + "targetPoint": { + "provider": "alibaba", + "region": "ap-northeast-1", + "credentialId": 1, + "path": "", + "bucket": "", + "endpoint": "https://oss-ap-northeast-1.aliyuncs.com", + "host": "dds-mj76dbe602390fa41311-pub.mongodb.ap-northeast-2.rds.aliyuncs.com", + "port": "3717", + "username": "root", + "password": "qwe123!@#", + "databaseName": "databases", + "databaseId": "", + "projectId": "" + } + }, + { + "meta": { + "serviceType": "nrdbms", + "taskType": "backup", + "taskId": "-task-0-20260417-180916" + }, + "dummy": { + "checkSQL": false, + "checkCSV": false, + "checkTXT": false, + "checkPNG": false, + "checkGIF": false, + "checkZIP": false, + "checkJSON": false, + "checkXML": false, + "checkServerJSON": false, + "checkServerSQL": false, + "sizeSQL": "", + "sizeCSV": "", + "sizeTXT": "", + "sizePNG": "", + "sizeGIF": "", + "sizeZIP": "", + "sizeJSON": "", + "sizeXML": "", + "sizeServerJSON": "", + "sizeServerSQL": "" + }, + "sourcePoint": { + "provider": "alibaba", + "region": "ap-northeast-1", + "credentialId": 1, + "path": "", + "bucket": "", + "endpoint": "https://oss-ap-northeast-1.aliyuncs.com", + "host": "dds-mj76dbe602390fa41311-pub.mongodb.ap-northeast-2.rds.aliyuncs.com", + "port": "3717", + "username": "root", + "password": "qwe123!@#", + "databaseName": "databases", + "databaseId": "", + "projectId": "" + }, + "targetPoint": { + "provider": "", + "region": "", + "credentialId": 0, + "path": "/Users/geontae/workspaces/namutech/mc-data-manager/data/backup", + "bucket": "", + "endpoint": "", + "host": "", + "port": "", + "username": "", + "password": "", + "databaseName": "", + "databaseId": "", + "projectId": "" + } + }, + { + "meta": { + "serviceType": "nrdbms", + "taskType": "restore", + "taskId": "-task-0-20260417-181053" + }, + "dummy": { + "checkSQL": false, + "checkCSV": false, + "checkTXT": false, + "checkPNG": false, + "checkGIF": false, + "checkZIP": false, + "checkJSON": false, + "checkXML": false, + "checkServerJSON": false, + "checkServerSQL": false, + "sizeSQL": "", + "sizeCSV": "", + "sizeTXT": "", + "sizePNG": "", + "sizeGIF": "", + "sizeZIP": "", + "sizeJSON": "", + "sizeXML": "", + "sizeServerJSON": "", + "sizeServerSQL": "" + }, + "sourcePoint": { + "provider": "", + "region": "", + "credentialId": 0, + "path": "/Users/geontae/workspaces/namutech/mc-data-manager/data/backup", + "bucket": "", + "endpoint": "", + "host": "", + "port": "", + "username": "", + "password": "", + "databaseName": "", + "databaseId": "", + "projectId": "" + }, + "targetPoint": { + "provider": "alibaba", + "region": "ap-northeast-1", + "credentialId": 1, + "path": "", + "bucket": "", + "endpoint": "https://oss-ap-northeast-1.aliyuncs.com", + "host": "dds-mj76dbe602390fa41311-pub.mongodb.ap-northeast-2.rds.aliyuncs.com", + "port": "3717", + "username": "root", + "password": "qwe123!@#", + "databaseName": "databases", + "databaseId": "", + "projectId": "" + } + }, + { + "meta": { + "serviceType": "nrdbms", + "taskType": "migrate", + "taskId": "-task-0-20260417-181209" + }, + "dummy": { + "checkSQL": false, + "checkCSV": false, + "checkTXT": false, + "checkPNG": false, + "checkGIF": false, + "checkZIP": false, + "checkJSON": false, + "checkXML": false, + "checkServerJSON": false, + "checkServerSQL": false, + "sizeSQL": "", + "sizeCSV": "", + "sizeTXT": "", + "sizePNG": "", + "sizeGIF": "", + "sizeZIP": "", + "sizeJSON": "", + "sizeXML": "", + "sizeServerJSON": "", + "sizeServerSQL": "" + }, + "sourcePoint": { + "provider": "alibaba", + "region": "", + "credentialId": 1, + "path": "", + "bucket": "", + "endpoint": "", + "host": "dds-mj76dbe602390fa41311-pub.mongodb.ap-northeast-2.rds.aliyuncs.com", + "port": "3717", + "username": "root", + "password": "qwe123!@#", + "databaseName": "databases", + "databaseId": "", + "projectId": "" + }, + "targetPoint": { + "provider": "alibaba", + "region": "", + "credentialId": 1, + "path": "", + "bucket": "", + "endpoint": "", + "host": "dds-mj76dbe602390fa41311-pub.mongodb.ap-northeast-2.rds.aliyuncs.com", + "port": "3717", + "username": "root", + "password": "qwe123!@#", + "databaseName": "databases2", + "databaseId": "", + "projectId": "" } } ], diff --git a/internal/auth/base.go b/internal/auth/base.go index 7e92f66..b703b0b 100644 --- a/internal/auth/base.go +++ b/internal/auth/base.go @@ -27,6 +27,7 @@ import ( "github.com/cloud-barista/mc-data-manager/config" "github.com/cloud-barista/mc-data-manager/models" + "github.com/cloud-barista/mc-data-manager/pkg/nrdbms/alibabamgdb" "github.com/cloud-barista/mc-data-manager/pkg/nrdbms/awsdnmdb" "github.com/cloud-barista/mc-data-manager/pkg/nrdbms/gcpfsdb" "github.com/cloud-barista/mc-data-manager/pkg/nrdbms/ncpmgdb" @@ -306,6 +307,24 @@ func GetNRDMS(params *models.ProviderConfig) (*nrdbc.NRDBController, error) { if err != nil { return nil, err } + case "alibaba": + log.Info().Str("Username", params.User).Msg("Alibaba Credentials") + log.Info().Str("Password", params.Password).Msg("Alibaba Credentials") + log.Info().Str("Host", params.Host).Msg("Alibaba Host") + log.Info().Str("Port", params.Port).Msg("Alibaba Port") + port, err := strconv.Atoi(params.Port) + if err != nil { + return nil, err + } + + alibabanrdb, err := config.NewAlibabaMongoDBClient(params.User, params.Password, params.Host, port) + if err != nil { + return nil, err + } + NRDBC, err = nrdbc.New(alibabamgdb.New(alibabanrdb, params.DatabaseName)) + if err != nil { + return nil, err + } } return NRDBC, nil } diff --git a/pkg/nrdbms/alibabamgdb/alibabamgdb.go b/pkg/nrdbms/alibabamgdb/alibabamgdb.go new file mode 100644 index 0000000..88d391e --- /dev/null +++ b/pkg/nrdbms/alibabamgdb/alibabamgdb.go @@ -0,0 +1,108 @@ +/* +Copyright 2023 The Cloud-Barista Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package alibabamgdb + +import ( + "context" + + "github.com/cloud-barista/mc-data-manager/models" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo" +) + +type AlibabaMongoDBMS struct { + provider models.Provider + dbName string + + client *mongo.Client + db *mongo.Database + ctx context.Context +} + +type AlibabaMongoDBOption func(*AlibabaMongoDBMS) + +func New(client *mongo.Client, databaseName string, opts ...AlibabaMongoDBOption) *AlibabaMongoDBMS { + dms := &AlibabaMongoDBMS{ + provider: models.ALIBABA, + dbName: databaseName, + client: client, + ctx: context.TODO(), + db: client.Database(databaseName), + } + + for _, opt := range opts { + opt(dms) + } + + return dms +} + +// list table +func (a *AlibabaMongoDBMS) ListTables() ([]string, error) { + return a.db.ListCollectionNames(a.ctx, bson.D{}) +} + +// delete table +func (a *AlibabaMongoDBMS) DeleteTables(tableName string) error { + return a.client.Database(a.dbName).Collection(tableName).Drop(a.ctx) +} + +// create table +func (a *AlibabaMongoDBMS) CreateTable(tableName string) error { + _, err := a.db.Collection(tableName).InsertOne(a.ctx, map[string]interface{}{}) + if err != nil { + return err + } + + _, err = a.db.Collection(tableName).DeleteOne(a.ctx, map[string]interface{}{}) + return err +} + +// import table +func (a *AlibabaMongoDBMS) ImportTable(tableName string, srcData *[]map[string]interface{}) error { + for _, data := range *srcData { + _, err := a.db.Collection(tableName).InsertOne(a.ctx, data) + if err != nil { + return err + } + } + return nil +} + +// export table +func (a *AlibabaMongoDBMS) ExportTable(tableName string, dstData *[]map[string]interface{}) error { + cursor, err := a.db.Collection(tableName).Find(a.ctx, map[string]interface{}{}) + if err != nil { + return err + } + defer cursor.Close(a.ctx) + + for cursor.Next(a.ctx) { + var result map[string]interface{} + err := cursor.Decode(&result) + if err != nil { + return err + } + + if oid, ok := result["_id"].(primitive.ObjectID); ok { + result["_id"] = oid.Hex() + } + + *dstData = append(*dstData, result) + } + return nil +} diff --git a/web/js/scripts.js b/web/js/scripts.js index da86e4a..0996e1b 100644 --- a/web/js/scripts.js +++ b/web/js/scripts.js @@ -705,12 +705,14 @@ function backUpFormSubmit() { alert("credential not selected"); return } - if(jsonData.sourcePoint.bucket == "none" || jsonData.sourcePoint.bucket == "-" || jsonData.sourcePoint.bucket == "") { + if(service == "objectstorage" && (jsonData.sourcePoint.bucket == "none" || jsonData.sourcePoint.bucket == "-" || jsonData.sourcePoint.bucket == "")) { alert("please select bucket"); return } if(service == "objectstorage") { applyFilter(jsonData) + } else { + delete jsonData.sourceFilter; } // console.log(provider) @@ -1022,7 +1024,7 @@ function toggleDivs(prefix, provider, service) { const mongoDiv = document.getElementById(prefix + "MongoDiv"); if (!regionDiv || !mongoDiv) return; - const showMongo = service === "nrdbms" && provider === "ncp"; + const showMongo = service === "nrdbms" && (provider === "ncp" || provider === "alibaba"); regionDiv.style.display = showMongo ? "none" : ""; mongoDiv.style.display = showMongo ? "" : "none"; @@ -1088,6 +1090,7 @@ function getServiceName(service, provider) { aws: "AWS DynamoDB", ncp: "Naver MongoDB", gcp: "Google Firestore", + alibaba: "Alibaba MongoDB", }, objectstorage: { aws: "AWS S3", diff --git a/web/templates/back-backup.html b/web/templates/back-backup.html index 9c18505..c95c086 100644 --- a/web/templates/back-backup.html +++ b/web/templates/back-backup.html @@ -121,7 +121,7 @@

Object Storage

- - @@ -125,7 +140,22 @@

Object Storage

{{ range $index, $value := .ALIBABARegions }} {{ end }} - + + + {{ range $index, $value := .IBMRegions }} + + {{ end }} + + + {{ range $index, $value := .KTRegions }} + + {{ end }} + + + {{ range $index, $value := .TencentRegions }} + + {{ end }} + diff --git a/web/templates/res-restore.html b/web/templates/res-restore.html index 3d4b3df..26cf627 100644 --- a/web/templates/res-restore.html +++ b/web/templates/res-restore.html @@ -78,7 +78,22 @@

Object Storage

{{ range $index, $value := .ALIBABARegions }} {{ end }} - + + + {{ range $index, $value := .IBMRegions }} + + {{ end }} + + + {{ range $index, $value := .KTRegions }} + + {{ end }} + + + {{ range $index, $value := .TencentRegions }} + + {{ end }} + diff --git a/websrc/controllers/pageHandlers.go b/websrc/controllers/pageHandlers.go index 78fabc1..00faac9 100644 --- a/websrc/controllers/pageHandlers.go +++ b/websrc/controllers/pageHandlers.go @@ -47,6 +47,9 @@ func DashBoardHandler(ctx echo.Context) error { GCPRegions: service.GetRegions("gcp"), NCPRegions: service.GetRegions("ncp"), ALIBABARegions: service.GetRegions("alibaba"), + IBMRegions: service.GetRegions("ibm"), + KTRegions: service.GetRegions("kt"), + TencentRegions: service.GetRegions("tencent"), OS: runtime.GOOS, Error: nil, }) @@ -65,6 +68,9 @@ func TaskRegisterHandler(ctx echo.Context) error { GCPRegions: service.GetRegions("gcp"), NCPRegions: service.GetRegions("ncp"), ALIBABARegions: service.GetRegions("alibaba"), + IBMRegions: service.GetRegions("ibm"), + KTRegions: service.GetRegions("kt"), + TencentRegions: service.GetRegions("tencent"), OS: runtime.GOOS, Error: nil, }) @@ -107,6 +113,9 @@ func GenerateObjectStorageGetHandler(ctx echo.Context) error { GCPRegions: service.GetRegions("gcp"), NCPRegions: service.GetRegions("ncp"), ALIBABARegions: service.GetRegions("alibaba"), + IBMRegions: service.GetRegions("ibm"), + KTRegions: service.GetRegions("kt"), + TencentRegions: service.GetRegions("tencent"), OS: runtime.GOOS, Error: nil, }) @@ -239,6 +248,9 @@ func BackupHandler(ctx echo.Context) error { GCPRegions: service.GetRegions("gcp"), NCPRegions: service.GetRegions("ncp"), ALIBABARegions: service.GetRegions("alibaba"), + IBMRegions: service.GetRegions("ibm"), + KTRegions: service.GetRegions("kt"), + TencentRegions: service.GetRegions("tencent"), OS: runtime.GOOS, Error: nil, }) @@ -256,6 +268,9 @@ func RestoreHandler(ctx echo.Context) error { GCPRegions: service.GetRegions("gcp"), NCPRegions: service.GetRegions("ncp"), ALIBABARegions: service.GetRegions("alibaba"), + IBMRegions: service.GetRegions("ibm"), + KTRegions: service.GetRegions("kt"), + TencentRegions: service.GetRegions("tencent"), OS: runtime.GOOS, Error: nil, }) @@ -269,10 +284,13 @@ func MigrationObjectStorageHandler(ctx echo.Context) error { logger.Info().Msg("migration object storage get page accessed") return ctx.Render(http.StatusOK, "index.html", models.BasicPageResponse{ Content: "Migration-Object-Storage", - GCPRegions: service.GetRegions("gcp"), AWSRegions: service.GetRegions("aws"), + GCPRegions: service.GetRegions("gcp"), NCPRegions: service.GetRegions("ncp"), ALIBABARegions: service.GetRegions("alibaba"), + IBMRegions: service.GetRegions("ibm"), + KTRegions: service.GetRegions("kt"), + TencentRegions: service.GetRegions("tencent"), OS: runtime.GOOS, Error: nil, }) From 616f0484ffb96648a59aee16dd8fced90f020b16 Mon Sep 17 00:00:00 2001 From: "minyeong.yang" Date: Fri, 24 Apr 2026 17:58:16 +0900 Subject: [PATCH 5/6] =?UTF-8?q?feat:=20alibaba=20rdb=20=EC=97=B0=EB=8F=99,?= =?UTF-8?q?=20rdb=20database=20=EB=8B=A8=EC=9C=84=20=EC=97=85=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/auth/rdb.go | 2 +- pkg/rdbms/mysql/mysql.go | 2 +- service/rdbc/rdbc.go | 26 +++++++++----------------- service/task/task.go | 10 +++++++++- web/js/scripts.js | 2 ++ web/templates/back-backup.html | 10 +++++++++- web/templates/gen-mysql.html | 1 + web/templates/mig-mysql.html | 6 ++++-- 8 files changed, 36 insertions(+), 23 deletions(-) diff --git a/internal/auth/rdb.go b/internal/auth/rdb.go index 7b59794..c9fcebc 100644 --- a/internal/auth/rdb.go +++ b/internal/auth/rdb.go @@ -137,7 +137,7 @@ func MigrationRDMFunc(datamoldParams *models.CommandTask) error { } log.Info().Msgf("Launch RDBController Copy") - if err := srcRDBC.Copy(dstRDBC); err != nil { + if err := srcRDBC.Copy(dstRDBC, datamoldParams.SourcePoint.DatabaseName); err != nil { log.Error().Msgf("Copy error copying into rdbms : %v", err) return err } diff --git a/pkg/rdbms/mysql/mysql.go b/pkg/rdbms/mysql/mysql.go index 16570ab..c8f300b 100644 --- a/pkg/rdbms/mysql/mysql.go +++ b/pkg/rdbms/mysql/mysql.go @@ -130,7 +130,7 @@ func (d *MysqlDBMS) ListDB(dst *[]string) error { return err } - if dbName != "information_schema" && dbName != "mysql" && dbName != "performance_schema" && dbName != "sys" { + if dbName != "information_schema" && dbName != "mysql" && dbName != "performance_schema" && dbName != "sys" && dbName != "__recycle_bin__" { *dst = append(*dst, dbName) } } diff --git a/service/rdbc/rdbc.go b/service/rdbc/rdbc.go index 33213a9..eb9c97d 100644 --- a/service/rdbc/rdbc.go +++ b/service/rdbc/rdbc.go @@ -126,28 +126,20 @@ func (rdb *RDBController) PutDoc(sql string) error { return nil } -// Migration using put and get -func (rdb *RDBController) Copy(dst *RDBController) error { - var dbList []string +// Migration using put and get for a specific database +func (rdb *RDBController) Copy(dst *RDBController, srcDbName string) error { var sql string - if err := rdb.ListDB(&dbList); err != nil { - rdb.logWrite("Error", "ListDB error", err) + rdb.Client.SetTargetProvdier(dst.Client.GetProvdier()) + if err := rdb.Get(srcDbName, &sql); err != nil { + rdb.logWrite("Error", "Get error", err) return err } - for _, db := range dbList { - sql = "" - rdb.Client.SetTargetProvdier(dst.Client.GetProvdier()) - if err := rdb.Get(db, &sql); err != nil { - rdb.logWrite("Error", "Get error", err) - return err - } - if err := dst.Put(sql); err != nil { - rdb.logWrite("Error", "Get error", err) - return err - } - rdb.logWrite("Info", fmt.Sprintf("Migration success: src:/%s -> dst:/%s", db, db), nil) + if err := dst.Put(sql); err != nil { + rdb.logWrite("Error", "Put error", err) + return err } + rdb.logWrite("Info", fmt.Sprintf("Migration success: src:/%s -> dst:/%s", srcDbName, srcDbName), nil) return nil } diff --git a/service/task/task.go b/service/task/task.go index 1c23abb..26340ac 100644 --- a/service/task/task.go +++ b/service/task/task.go @@ -9,6 +9,7 @@ import ( "os" "path/filepath" "regexp" + "slices" "strings" "sync" "time" @@ -988,7 +989,7 @@ func handleRDBMSMigrateTask(params models.BasicDataTask) models.Status { } log.Info().Msg("Launch RDBController Copy") - if err := srcRDBC.Copy(dstRDBC); err != nil { + if err := srcRDBC.Copy(dstRDBC, params.SourcePoint.DatabaseName); err != nil { log.Error().Err(err).Msg("Copy error copying into rdbms ") return models.StatusFailed } @@ -1020,6 +1021,13 @@ func handleRDBMSBackupTask(params models.BasicDataTask) models.Status { return models.StatusFailed } + sourceDB := params.SourcePoint.DatabaseName + if !slices.Contains(dbList, sourceDB) { + log.Error().Msgf("database %q not found in server", sourceDB) + return models.StatusFailed + } + dbList = []string{sourceDB} + var sqlData string for _, db := range dbList { sqlData = "" diff --git a/web/js/scripts.js b/web/js/scripts.js index da86e4a..2a5595a 100644 --- a/web/js/scripts.js +++ b/web/js/scripts.js @@ -711,6 +711,8 @@ function backUpFormSubmit() { } if(service == "objectstorage") { applyFilter(jsonData) + } else { + delete jsonData.sourceFilter; // rdbms/nrdbms면 filter 제거 } // console.log(provider) diff --git a/web/templates/back-backup.html b/web/templates/back-backup.html index 9c18505..73dab50 100644 --- a/web/templates/back-backup.html +++ b/web/templates/back-backup.html @@ -90,6 +90,7 @@

Object Storage

+
@@ -113,13 +114,20 @@

Object Storage

-
+
+
+ +
+ + +
+
@@ -84,7 +85,7 @@

SQL Database

- +
@@ -223,6 +224,7 @@

SQL Database

+ @@ -257,7 +259,7 @@

SQL Database

- +
From e6ec950624293964b40e025626d7e978743f5b09 Mon Sep 17 00:00:00 2001 From: coli-bear Date: Mon, 27 Apr 2026 16:20:28 +0900 Subject: [PATCH 6/6] =?UTF-8?q?feat:=20alibaba/ncp=20-=20region=20?= =?UTF-8?q?=ED=95=84=EC=88=98=EA=B0=92=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/var/run/data-manager/task/task.json | 238 +++++++++++++++++++++++ docker-compose.yaml | 18 ++ web/js/scripts.js | 5 +- web/templates/back-backup.html | 16 ++ web/templates/gen-no-sql.html | 16 ++ web/templates/res-restore.html | 16 ++ 6 files changed, 308 insertions(+), 1 deletion(-) diff --git a/data/var/run/data-manager/task/task.json b/data/var/run/data-manager/task/task.json index a971a64..fb968c1 100644 --- a/data/var/run/data-manager/task/task.json +++ b/data/var/run/data-manager/task/task.json @@ -7399,6 +7399,244 @@ "databaseId": "", "projectId": "" } + }, + { + "meta": { + "serviceType": "nrdbms", + "taskType": "generate", + "taskId": "-task-0-20260424-141931" + }, + "dummy": { + "dummyPath": "/tmp/datamold-dummy893324966", + "checkSQL": false, + "checkCSV": false, + "checkTXT": false, + "checkPNG": false, + "checkGIF": false, + "checkZIP": false, + "checkJSON": false, + "checkXML": false, + "checkServerJSON": true, + "checkServerSQL": false, + "sizeSQL": "", + "sizeCSV": "", + "sizeTXT": "", + "sizePNG": "", + "sizeGIF": "", + "sizeZIP": "", + "sizeJSON": "", + "sizeXML": "", + "sizeServerJSON": "1", + "sizeServerSQL": "" + }, + "sourcePoint": { + "provider": "", + "region": "", + "credentialId": 0, + "path": "", + "bucket": "", + "endpoint": "", + "host": "", + "port": "", + "username": "", + "password": "", + "databaseName": "", + "databaseId": "", + "projectId": "" + }, + "targetPoint": { + "provider": "alibaba", + "region": "", + "credentialId": 1, + "path": "", + "bucket": "", + "endpoint": "", + "host": "10.10.3.62", + "port": "27017", + "username": "root", + "password": "qwe123!@#", + "databaseName": "databases", + "databaseId": "", + "projectId": "" + } + }, + { + "meta": { + "serviceType": "nrdbms", + "taskType": "backup", + "taskId": "-task-0-20260424-143041" + }, + "dummy": { + "checkSQL": false, + "checkCSV": false, + "checkTXT": false, + "checkPNG": false, + "checkGIF": false, + "checkZIP": false, + "checkJSON": false, + "checkXML": false, + "checkServerJSON": false, + "checkServerSQL": false, + "sizeSQL": "", + "sizeCSV": "", + "sizeTXT": "", + "sizePNG": "", + "sizeGIF": "", + "sizeZIP": "", + "sizeJSON": "", + "sizeXML": "", + "sizeServerJSON": "", + "sizeServerSQL": "" + }, + "sourcePoint": { + "provider": "alibaba", + "region": "", + "credentialId": 1, + "path": "", + "bucket": "", + "endpoint": "https://oss-ap-northeast-1.aliyuncs.com", + "host": "10.10.3.63", + "port": "27017", + "username": "root", + "password": "qwe123!@#", + "databaseName": "databases", + "databaseId": "", + "projectId": "" + }, + "targetPoint": { + "provider": "", + "region": "", + "credentialId": 0, + "path": "/Users/geontae/workspaces/namutech/mc-data-manager/data/backup", + "bucket": "", + "endpoint": "", + "host": "", + "port": "", + "username": "", + "password": "", + "databaseName": "", + "databaseId": "", + "projectId": "" + } + }, + { + "meta": { + "serviceType": "nrdbms", + "taskType": "generate", + "taskId": "-task-0-20260424-143215" + }, + "dummy": { + "dummyPath": "/var/folders/8d/dykp7pg5693fhf5_72hr16540000gn/T/datamold-dummy1848932528", + "checkSQL": false, + "checkCSV": false, + "checkTXT": false, + "checkPNG": false, + "checkGIF": false, + "checkZIP": false, + "checkJSON": false, + "checkXML": false, + "checkServerJSON": true, + "checkServerSQL": false, + "sizeSQL": "", + "sizeCSV": "", + "sizeTXT": "", + "sizePNG": "", + "sizeGIF": "", + "sizeZIP": "", + "sizeJSON": "", + "sizeXML": "", + "sizeServerJSON": "1", + "sizeServerSQL": "" + }, + "sourcePoint": { + "provider": "", + "region": "", + "credentialId": 0, + "path": "", + "bucket": "", + "endpoint": "", + "host": "", + "port": "", + "username": "", + "password": "", + "databaseName": "", + "databaseId": "", + "projectId": "" + }, + "targetPoint": { + "provider": "alibaba", + "region": "ap-northeast-1", + "credentialId": 1, + "path": "", + "bucket": "", + "endpoint": "https://oss-ap-northeast-1.aliyuncs.com", + "host": "10.10.3.62", + "port": "27017", + "username": "root", + "password": "qwe123!@#", + "databaseName": "databases", + "databaseId": "", + "projectId": "" + } + }, + { + "meta": { + "serviceType": "nrdbms", + "taskType": "backup", + "taskId": "-task-0-20260424-143250" + }, + "dummy": { + "checkSQL": false, + "checkCSV": false, + "checkTXT": false, + "checkPNG": false, + "checkGIF": false, + "checkZIP": false, + "checkJSON": false, + "checkXML": false, + "checkServerJSON": false, + "checkServerSQL": false, + "sizeSQL": "", + "sizeCSV": "", + "sizeTXT": "", + "sizePNG": "", + "sizeGIF": "", + "sizeZIP": "", + "sizeJSON": "", + "sizeXML": "", + "sizeServerJSON": "", + "sizeServerSQL": "" + }, + "sourcePoint": { + "provider": "alibaba", + "region": "", + "credentialId": 1, + "path": "", + "bucket": "", + "endpoint": "https://oss-ap-northeast-1.aliyuncs.com", + "host": "10.10.3.63", + "port": "27017", + "username": "root", + "password": "qwe123!@#", + "databaseName": "databases", + "databaseId": "", + "projectId": "" + }, + "targetPoint": { + "provider": "", + "region": "", + "credentialId": 0, + "path": "/Users/geontae/workspaces/namutech/mc-data-manager/data/backup", + "bucket": "", + "endpoint": "", + "host": "", + "port": "", + "username": "", + "password": "", + "databaseName": "", + "databaseId": "", + "projectId": "" + } } ], "flows": [], diff --git a/docker-compose.yaml b/docker-compose.yaml index 61164e4..a06f05f 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -67,6 +67,24 @@ services: - MYSQL_ROOT_PASSWORD=mcmp + mc-data-manager-mongodb: + image: mongo:6 + container_name: mc-data-manager-mongodb + ports: + - "27017:27017" + environment: + - MONGO_INITDB_ROOT_USERNAME=root + - MONGO_INITDB_ROOT_PASSWORD=qwe123!@# + - MONGO_INITDB_DATABASE=databases + volumes: + - ./data/mongodb:/data/db + restart: always + healthcheck: + test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"] + interval: 30s + timeout: 10s + retries: 3 + ################## ## OPTIONAL ## ################## diff --git a/web/js/scripts.js b/web/js/scripts.js index 0996e1b..4065759 100644 --- a/web/js/scripts.js +++ b/web/js/scripts.js @@ -1031,7 +1031,10 @@ function toggleDivs(prefix, provider, service) { const regionSelect = regionDiv.querySelector("select"); if (regionSelect) { - regionSelect.disabled = showMongo; // mongoDiv 보이면 regionSelect 비활성화 + regionSelect.disabled = showMongo; + if (showMongo) { + regionSelect.removeAttribute('required'); + } } // mongoDiv 하위 모든 input 제어 const mongoInputs = mongoDiv.querySelectorAll("input"); diff --git a/web/templates/back-backup.html b/web/templates/back-backup.html index c95c086..4fdccd1 100644 --- a/web/templates/back-backup.html +++ b/web/templates/back-backup.html @@ -282,6 +282,22 @@

Object Storage

input.setAttribute('required', ''); }) + // No-SQL + NCP/Alibaba 인 경우 Region 선택 불필요 + const regionSelectWrapper = document.getElementById("sourceRegionSelect"); + const regionInputGroup = regionSelectWrapper ? regionSelectWrapper.closest(".input-group") : null; + const isNoSqlNonRegion = service === 'nrdbms' && (source.provider.value === 'ncp' || source.provider.value === 'alibaba'); + if (regionInputGroup) { + if (isNoSqlNonRegion) { + regionInputGroup.style.display = "none"; + regionSelectWrapper.removeAttribute('required'); + regionSelectWrapper.setAttribute('disabled', ''); + } else { + regionInputGroup.style.display = ""; + regionSelectWrapper.removeAttribute('disabled'); + regionSelectWrapper.setAttribute('required', ''); + } + } + document.getElementById("rdbmsProvider").style.display = "none" } diff --git a/web/templates/gen-no-sql.html b/web/templates/gen-no-sql.html index 3d83aa1..f8eeeeb 100644 --- a/web/templates/gen-no-sql.html +++ b/web/templates/gen-no-sql.html @@ -170,6 +170,22 @@

No-SQL

updateLabel(provider) + // NCP / Alibaba 인 경우 Region 선택 불필요 + const regionSelect = document.getElementById('targetRegionSelect'); + const regionInputGroup = regionSelect ? regionSelect.closest('.input-group') : null; + const isNonRegion = provider === 'ncp' || provider === 'alibaba'; + if (regionInputGroup) { + if (isNonRegion) { + regionInputGroup.style.display = 'none'; + regionSelect.removeAttribute('required'); + regionSelect.setAttribute('disabled', ''); + } else { + regionInputGroup.style.display = ''; + regionSelect.removeAttribute('disabled'); + regionSelect.setAttribute('required', ''); + } + } + if(provider === 'gcp') { gcpDiv.style.display = ''; gcpDiv.querySelectorAll('input').forEach(i => { diff --git a/web/templates/res-restore.html b/web/templates/res-restore.html index 60b914c..c5a7f74 100644 --- a/web/templates/res-restore.html +++ b/web/templates/res-restore.html @@ -295,6 +295,22 @@

Object Storage

input.setAttribute('required', ''); }) + // No-SQL + NCP/Alibaba 인 경우 Region 선택 불필요 + const regionSelectWrapper = document.getElementById("targetRegionSelect"); + const regionInputGroup = regionSelectWrapper ? regionSelectWrapper.closest(".input-group") : null; + const isNoSqlNonRegion = service === 'nrdbms' && (provider === 'ncp' || provider === 'alibaba'); + if (regionInputGroup) { + if (isNoSqlNonRegion) { + regionInputGroup.style.display = "none"; + regionSelectWrapper.removeAttribute('required'); + regionSelectWrapper.setAttribute('disabled', ''); + } else { + regionInputGroup.style.display = ""; + regionSelectWrapper.removeAttribute('disabled'); + regionSelectWrapper.setAttribute('required', ''); + } + } + document.getElementById("rdbmsProvider").style.display = "none" }